resolved conflicts for merge of 72b26859 to lmp-mr1-ub-dev

Change-Id: Id188dacbfd8e83fd884f838e392444890a231e5e
diff --git a/Android.mk b/Android.mk
index fd9bcd1..dcc783e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -19,7 +19,7 @@
 # Subprojects with separate makefiles
 #
 
-subdirs := benchmarks
+subdirs := benchmarks tzdata
 subdir_makefiles := $(call all-named-subdir-makefiles,$(subdirs))
 
 #
diff --git a/Docs.mk b/Docs.mk
index a163d1f..0c52bf5 100644
--- a/Docs.mk
+++ b/Docs.mk
@@ -20,11 +20,20 @@
    libart/src/main/java/dalvik \
    libart/src/main/java/java \
    luni/src/main/java/android \
-   luni/src/main/java/java \
    luni/src/main/java/javax \
    luni/src/main/java/org/xml/sax \
    luni/src/main/java/org/w3c \
    xml/src/main/java/org/xmlpull/v1)
 
+# IcuIteratorWrapper.java references com.ibm.icu.text.BreakIterator,
+# which is renamed by our jarjar rule, and so unrecognizable by javadoc,
+# with annoying error: error: package com.ibm.icu.text does not exist.
+# We don't want to generate doc for this file anyway.
+libcore_to_document += \
+  $(filter-out luni/src/main/java/java/text/IcuIteratorWrapper.java,\
+    $(call find-files-in-subdirs, libcore, \
+      "*.java", \
+      luni/src/main/java/java))
+
 libcore_docs_include_once := 1
 endif # libcore_docs_include_once
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 57a8f82..9f6d827 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -69,14 +69,43 @@
 local_javac_flags+=-Xmaxwarns 9999999
 
 #
+# ICU4J related rules.
+#
+# We compile icu4j along with core-libart because we're implementing parts of core-libart
+# in terms of icu4j.
+icu4j_root := ../external/icu/icu4j/
+icu4j_src_files := $(call all-java-files-under,$(icu4j_root)/main/classes)
+
+# Filter out bits of ICU4J we don't use yet : the SPIs (which we have limited support for),
+# the charset encoders and the transliterators.
+icu4j_src_files := $(filter-out $(icu4j_root)/main/classes/localespi/%, $(icu4j_src_files))
+icu4j_src_files := $(filter-out $(icu4j_root)/main/classes/charset/%, $(icu4j_src_files))
+icu4j_src_files := $(filter-out $(icu4j_root)/main/classes/translit/%, $(icu4j_src_files))
+
+# Not all src dirs contain resources, some instead contain other random files
+# that should not be included as resources. The ones that should be included
+# can be identifed by the fact that they contain particular subdir trees.
+#
+define all-icu-subdir-with-subdir
+$(patsubst $(LOCAL_PATH)/%/$(2),%,$(wildcard $(LOCAL_PATH)/$(1)/$(2)))
+endef
+
+icu4j_resource_dirs := $(call all-icu-subdir-with-subdir,$(icu4j_root)/main/classes/*/src,com/ibm/icu)
+icu4j_resource_dirs := $(filter-out $(icu4j_root)/main/classes/localespi/%, $(icu4j_resource_dirs))
+icu4j_resource_dirs := $(filter-out $(icu4j_root)/main/classes/charset/%, $(icu4j_resource_dirs))
+icu4j_resource_dirs := $(filter-out $(icu4j_root)/main/classes/translit/%, $(icu4j_resource_dirs))
+
+
+
+#
 # Build for the target (device).
 #
 
 # Definitions to make the core library.
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libart_core_src_files)
-LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+LOCAL_SRC_FILES := $(libart_core_src_files) $(icu4j_src_files)
+LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) $(icu4j_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
 LOCAL_DX_FLAGS := --core-library
@@ -84,8 +113,19 @@
 LOCAL_MODULE := core-libart
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
 LOCAL_REQUIRED_MODULES := tzdata
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
 include $(BUILD_JAVA_LIBRARY)
 
+# Path to the ICU4C data files in the Android device file system:
+icu4c_data := /system/usr/icu
+# TODO: It's quite hideous that this double-slash between icu4j and main is required.
+# It's because we provide a variable substition of the make-rule generated jar command
+# to substitute a processed ICUProperties.config file in place of the original.
+#
+# We can avoid this by filtering out ICUConfig.properties from our list of resources.
+icu4j_config_root := $(LOCAL_PATH)/../external/icu/icu4j//main/classes/core/src
+include external/icu/icu4j/adjust_icudt_path.mk
+
 ifeq ($(LIBCORE_SKIP_TESTS),)
 # Make the core-tests library.
 include $(CLEAR_VARS)
@@ -126,25 +166,12 @@
 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
-# on-the-fly.
-TMP_RESOURCE_DIR := $(intermediates.COMMON)/tmp/
-TMP_RESOURCE_FILE := org/apache/harmony/luni/tests/java/lang/test\#.properties
-
-$(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE):
-	@mkdir -p $(dir $@)
-	@echo "Hello, world!" > $@
-
-$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args) -C "$(TMP_RESOURCE_DIR)" "$(TMP_RESOURCE_FILE)"
-$(LOCAL_INTERMEDIATE_TARGETS): $(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE)
-
-
 #
 # Build for the host.
 #
 
+ifeq ($(HOST_OS),linux)
+
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-main-java-files-under, dex)
 LOCAL_MODULE_TAGS := optional
@@ -153,8 +180,8 @@
 
 # Definitions to make the core library.
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libart_core_src_files)
-LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+LOCAL_SRC_FILES := $(libart_core_src_files) $(icu4j_src_files)
+LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) $(icu4j_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
 LOCAL_DX_FLAGS := --core-library
@@ -162,6 +189,7 @@
 LOCAL_MODULE := core-libart-hostdex
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
 LOCAL_REQUIRED_MODULES := tzdata-host
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
 include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
 
 # Make the core-tests library.
@@ -193,6 +221,8 @@
     include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
 endif
 
+endif # HOST_OS == linux
+
 #
 # Local droiddoc for faster libcore testing
 #
diff --git a/NOTICE b/NOTICE
index 951e506..5136b4b 100644
--- a/NOTICE
+++ b/NOTICE
@@ -104,3 +104,14 @@
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See W3C License http://www.w3.org/Consortium/Legal/ for more details.
+
+   =========================================================================
+   ==  NOTICE file for the fdlibm License.                                   ==
+   =========================================================================
+
+Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+
+Developed at SunSoft, a Sun Microsystems, Inc. business.
+Permission to use, copy, modify, and distribute this
+software is freely granted, provided that this notice
+is preserved.
diff --git a/NativeCode.mk b/NativeCode.mk
index 0ae615e..910527c 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -67,11 +67,10 @@
 core_shared_libraries := $(LOCAL_SHARED_LIBRARIES)
 core_static_libraries := $(LOCAL_STATIC_LIBRARIES)
 core_cflags := $(LOCAL_CFLAGS) -Wall -Wextra -Werror
-core_cppflags += -std=gnu++11
+core_cppflags += -std=gnu++11 -DU_USING_ICU_NAMESPACE=0
 
 core_test_files := \
   luni/src/test/native/dalvik_system_JniTest.cpp \
-  luni/src/test/native/test_openssl_engine.cpp \
 
 #
 # Build for the target (device).
@@ -87,7 +86,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libjavacore
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
-include external/stlport/libstlport.mk
+LOCAL_CXX_STL := libc++
 include $(BUILD_SHARED_LIBRARY)
 
 # Test JNI library.
@@ -97,21 +96,54 @@
 LOCAL_CFLAGS += $(core_cflags)
 LOCAL_CPPFLAGS += $(core_cppflags)
 LOCAL_SRC_FILES += $(core_test_files)
-LOCAL_C_INCLUDES += libcore/include external/openssl/include
+LOCAL_C_INCLUDES += libcore/include
 LOCAL_SHARED_LIBRARIES += libcrypto
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libjavacoretests
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
-include external/stlport/libstlport.mk
+LOCAL_CXX_STL := libc++
 include $(BUILD_SHARED_LIBRARY)
 
 endif # LIBCORE_SKIP_TESTS
 
+# Set of gtest unit tests.
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
+LOCAL_SRC_FILES += \
+  luni/src/test/native/libcore_io_Memory_test.cpp \
+
+LOCAL_C_INCLUDES += libcore/include
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := libjavacore-unit-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+LOCAL_CXX_STL := libc++
+include $(BUILD_NATIVE_TEST)
+
+# Set of benchmarks for libjavacore functions.
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
+LOCAL_SRC_FILES += \
+  luni/src/benchmark/native/libcore_io_Memory_bench.cpp \
+
+LOCAL_C_INCLUDES += libcore/include bionic/benchmarks
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := libjavacore-benchmarks
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+LOCAL_CXX_STL := libc++
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+include $(BUILD_NATIVE_BENCHMARK)
+
 
 #
 # Build for the host.
 #
 
+ifeq ($(HOST_OS),linux)
+
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_SRC_FILES += $(core_src_files)
@@ -128,6 +160,7 @@
 LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libexpat-host libicuuc-host libicui18n-host libcrypto-host libz-host
 LOCAL_STATIC_LIBRARIES += $(core_static_libraries) libziparchive-host libutils
 LOCAL_MULTILIB := both
+LOCAL_CXX_STL := libc++
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 ifeq ($(LIBCORE_SKIP_TESTS),)
@@ -135,12 +168,15 @@
     LOCAL_CLANG := true
     LOCAL_SRC_FILES += $(core_test_files)
     LOCAL_CFLAGS += $(core_cflags)
-    LOCAL_C_INCLUDES += libcore/include external/openssl/include
+    LOCAL_C_INCLUDES += libcore/include
     LOCAL_CPPFLAGS += $(core_cppflags)
     LOCAL_LDLIBS += -ldl -lpthread
     LOCAL_MODULE_TAGS := optional
     LOCAL_MODULE := libjavacoretests
     LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
     LOCAL_SHARED_LIBRARIES := libcrypto-host
+    LOCAL_CXX_STL := libc++
     include $(BUILD_HOST_SHARED_LIBRARY)
 endif # LIBCORE_SKIP_TESTS
+
+endif # HOST_OS == linux
diff --git a/benchmarks/Android.mk b/benchmarks/Android.mk
index c0a38a0..9e650918 100644
--- a/benchmarks/Android.mk
+++ b/benchmarks/Android.mk
@@ -21,7 +21,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := benchmarks
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := caliper-prebuilt core-tests
+LOCAL_STATIC_JAVA_LIBRARIES := caliper-prebuilt mockwebserver core-tests-support
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core-libart conscrypt core-junit bouncycastle framework
 LOCAL_MODULE_TAGS := tests
diff --git a/benchmarks/src/benchmarks/regression/BreakIteratorBenchmark.java b/benchmarks/src/benchmarks/regression/BreakIteratorBenchmark.java
new file mode 100644
index 0000000..8602ddc
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/BreakIteratorBenchmark.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+import java.text.BreakIterator;
+import java.util.Locale;
+
+public final class BreakIteratorBenchmark extends SimpleBenchmark {
+
+    public static enum Text {
+        LIPSUM(Locale.US, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi mollis consequat nisl non pharetra. Praesent pretium vehicula odio sed ultrices. Aenean a felis libero. Vivamus sed commodo nibh. Pellentesque turpis lectus, euismod vel ante nec, cursus posuere orci. Suspendisse velit neque, fermentum luctus ultrices in, ultrices vitae arcu. Duis tincidunt cursus lorem. Nam ultricies accumsan quam vitae imperdiet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque aliquet pretium nisi, eget laoreet enim molestie sit amet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.\nNam dapibus aliquam lacus ac suscipit. Proin in nibh sit amet purus congue laoreet eget quis nisl. Morbi gravida dignissim justo, a venenatis ante pulvinar at. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ultrices vestibulum dui, vel aliquam lacus aliquam quis. Duis fringilla sapien ac lacus egestas, vel adipiscing elit euismod. Donec non tellus odio. Donec gravida eu massa ac feugiat. Aliquam erat volutpat. Praesent id adipiscing metus, nec laoreet enim. Aliquam vitae posuere turpis. Mauris ac pharetra sem. In at placerat tortor. Vivamus ac vehicula neque. Cras volutpat ullamcorper massa et varius. Praesent sagittis neque vitae nulla euismod pharetra.\nSed placerat sapien non molestie sollicitudin. Nullam sit amet dictum quam. Etiam tincidunt tortor vel pretium vehicula. Praesent fringilla ipsum vel velit luctus dignissim. Nulla massa ligula, mattis in enim et, mattis lacinia odio. Suspendisse tristique urna a orci commodo tempor. Duis lacinia egestas arcu a sollicitudin.\nIn ac feugiat lacus. Nunc fermentum eu est at tristique. Pellentesque quis ligula et orci placerat lacinia. Maecenas quis mauris diam. Etiam mi ipsum, tempus in purus quis, euismod faucibus orci. Nulla facilisi. Praesent sit amet sapien vel elit porta adipiscing. Phasellus sit amet volutpat diam.\nProin bibendum elit non lacus pharetra, quis eleifend tellus placerat. Nulla facilisi. Maecenas ante diam, pellentesque mattis mattis in, porta ut lorem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc interdum tristique metus, in scelerisque odio fermentum eget. Cras nec venenatis lacus. Aenean euismod eget metus quis molestie. Cras tincidunt dolor ut massa ornare, in elementum lacus auctor. Cras sodales nisl lacus, id ultrices ligula varius at. Sed tristique sit amet tellus vel mollis. Sed sed sollicitudin quam. Sed sed adipiscing risus, et dictum orci. Cras tempor pellentesque turpis et tempus."),
+        LONGPARA(Locale.US, "During dinner, Mr. Bennet scarcely spoke at all; but when the servants were withdrawn, he thought it time to have some conversation with his guest, and therefore started a subject in which he expected him to shine, by observing that he seemed very fortunate in his patroness. Lady Catherine de Bourgh's attention to his wishes, and consideration for his comfort, appeared very remarkable. Mr. Bennet could not have chosen better. Mr. Collins was eloquent in her praise. The subject elevated him to more than usual solemnity of manner, and with a most important aspect he protested that \"he had never in his life witnessed such behaviour in a person of rank--such affability and condescension, as he had himself experienced from Lady Catherine. She had been graciously pleased to approve of both of the discourses which he had already had the honour of preaching before her. She had also asked him twice to dine at Rosings, and had sent for him only the Saturday before, to make up her pool of quadrille in the evening. Lady Catherine was reckoned proud by many people he knew, but _he_ had never seen anything but affability in her. She had always spoken to him as she would to any other gentleman; she made not the smallest objection to his joining in the society of the neighbourhood nor to his leaving the parish occasionally for a week or two, to visit his relations. She had even condescended to advise him to marry as soon as he could, provided he chose with discretion; and had once paid him a visit in his humble parsonage, where she had perfectly approved all the alterations he had been making, and had even vouchsafed to suggest some herself--some shelves in the closet up stairs.\""),
+        GERMAN(Locale.GERMANY, "Aber dieser Freiheit setzte endlich der Winter ein Ziel. Draußen auf den Feldern und den hohen Bergen lag der Schnee und Peter wäre in seinem dünnen Leinwandjäckchen bald erfroren. Es war also seine einzige Freude, hinaus vor die Hütte zu treten und den Sperlingen Brotkrümchen zu streuen, was er sich jedesmal an seinem Frühstück absparte. Wenn nun die Vögel so lustig zwitscherten und um ihn herumflogen, da klopfte ihm das Herz vor Lust, und oft gab er ihnen sein ganzes Stück Schwarzbrot, ohne daran zu denken, daß er dafür alsdann selbst hungern müsse."),
+        THAI(Locale.forLanguageTag("th-TH"), "เป็นสำเนียงทางการของภาษาไทย เดิมทีเป็นการผสมผสานกันระหว่างสำเนียงอยุธยาและชาวไทยเชื้อสายจีนรุ่นหลังที่พูดไทยแทนกลุ่มภาษาจีน ลักษณะเด่นคือมีการออกเสียงที่ชัดเจนและแข็งกระด้างซึ่งได้รับอิทธิพลจากภาษาแต้จิ๋ว การออกเสียงพยัญชนะ สระ การผันวรรณยุกต์ที่ในภาษาไทยมาตรฐาน มาจากสำเนียงถิ่นนี้ในขณะที่ภาษาไทยสำเนียงอื่นล้วนเหน่อทั้งสิ้น คำศัพท์ที่ใช้ในสำเนียงกรุงเทพจำนวนมากได้รับมาจากกลุ่มภาษาจีนเช่นคำว่า โป๊, เฮ็ง, อาหมวย, อาซิ่ม ซึ่งมาจากภาษาแต้จิ๋ว และจากภาษาจีนเช่น ถู(涂), ชิ่ว(去 อ่านว่า\"ชู่\") และคำว่า ทาย(猜 อ่านว่า \"ชาย\") เป็นต้น เนื่องจากสำเนียงกรุงเทพได้รับอิทธิพลมาจากภาษาจีนดังนั้นตัวอักษร \"ร\" มักออกเสียงเหมารวมเป็น \"ล\" หรือคำควบกล่ำบางคำถูกละทิ้งไปด้วยเช่น รู้ เป็น ลู้, เรื่อง เป็น เลื่อง หรือ ประเทศ เป็น ปะเทศ เป็นต้นสร้างความลำบากให้แก่ต่างชาติที่ต้องการเรียนภาษาไทย แต่อย่างไรก็ตามผู้ที่พูดสำเนียงถิ่นนี้ก็สามารถออกอักขระภาษาไทยตามมาตรฐานได้อย่างถูกต้องเพียงแต่มักเผลอไม่ค่อยออกเสียง"),
+        THAI2(Locale.forLanguageTag("th-TH"), "this is the word browser in Thai: เบราว์เซอร์"),
+        TABS(Locale.US, "one\t\t\t\t\t\t\t\t\t\t\t\t\t\ttwo\n"),
+        ACCENT(Locale.US, "e\u0301\u00e9\nwhich is:\n\"e\\u0301\\u00e9\""),
+        EMOJI(Locale.US, ">>\ud83d\ude01<<\nwhich is:\n\">>\\ud83d\\ude01<<\""),
+        SPACES(Locale.US, "     leading spaces      and trailing ones too      "),
+        EMPTY(Locale.US, ""),
+        NEWLINE(Locale.US, "\\n:\n"),
+        BIDI(Locale.forLanguageTag("he-IL"), "Sarah שרה is spelled sin ש resh ר heh ה from right to left.");
+
+        final Locale locale;
+        final String text;
+
+        Text(Locale locale, String text) {
+            this.text  = text;
+            this.locale = locale;
+        }
+    }
+
+    @Param private Text text;
+
+    public void timeBreakIterator(int nreps) {
+        for (int i = 0;  i < nreps; ++i) {
+            BreakIterator it = BreakIterator.getLineInstance(text.locale);
+            it.setText(text.text);
+
+            while (it.next() != BreakIterator.DONE) {
+            }
+        }
+    }
+
+    public void timeIcuBreakIterator(int nreps) {
+        for (int i = 0; i < nreps; ++i) {
+            android.icu.text.BreakIterator it =
+                android.icu.text.BreakIterator.getLineInstance(text.locale);
+            it.setText(text.text);
+
+            while (it.next() != android.icu.text.BreakIterator.DONE) {
+            }
+        }
+    }
+}
diff --git a/benchmarks/src/benchmarks/regression/CollectionsBenchmark.java b/benchmarks/src/benchmarks/regression/CollectionsBenchmark.java
new file mode 100644
index 0000000..18b969a
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/CollectionsBenchmark.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Random;
+import java.util.Vector;
+
+public class CollectionsBenchmark extends SimpleBenchmark {
+    @Param({"4", "16", "64", "256", "1024"})
+    private int arrayListLength;
+
+    public static Comparator<Integer> REVERSE = new Comparator<Integer>() {
+        @Override
+        public int compare(Integer lhs, Integer rhs) {
+            int lhsAsInt = lhs.intValue();
+            int rhsAsInt = rhs.intValue();
+            return rhsAsInt < lhsAsInt ? -1 : (lhsAsInt == rhsAsInt ? 0 : 1);
+        }
+    };
+
+
+    public void timeSort_arrayList(int nreps) throws Exception {
+        List<Integer> input = buildList(arrayListLength, ArrayList.class);
+        for (int i = 0; i < nreps; ++i) {
+            Collections.sort(input);
+        }
+    }
+
+    public void timeSortWithComparator_arrayList(int nreps) throws Exception {
+        List<Integer> input = buildList(arrayListLength, ArrayList.class);
+        for (int i = 0; i < nreps; ++i) {
+            Collections.sort(input, REVERSE);
+        }
+    }
+
+    public void timeSort_vector(int nreps) throws Exception {
+        List<Integer> input = buildList(arrayListLength, Vector.class);
+        for (int i = 0; i < nreps; ++i) {
+            Collections.sort(input);
+        }
+    }
+
+    public void timeSortWithComparator_vector(int nreps) throws Exception {
+        List<Integer> input = buildList(arrayListLength, Vector.class);
+        for (int i = 0; i < nreps; ++i) {
+            Collections.sort(input, REVERSE);
+        }
+    }
+
+    private static <T extends List<Integer>> List<Integer> buildList(
+            int arrayListLength, Class<T> listClass) throws Exception {
+        Random random = new Random();
+        random.setSeed(0);
+        List<Integer> list = listClass.newInstance();
+        for (int i = 0; i < arrayListLength; ++i) {
+            list.add(random.nextInt());
+        }
+        return list;
+    }
+}
diff --git a/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java
index 02d8f97..e904b4d 100644
--- a/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java
@@ -18,39 +18,41 @@
 
 import com.google.caliper.SimpleBenchmark;
 
-import java.util.Locale;
-import java.util.TimeZone;
+import android.icu.util.ULocale;
+import android.icu.util.TimeZone;
 
-import static libcore.icu.DateIntervalFormat.*;
+import libcore.icu.DateIntervalFormat;
+
+import static libcore.icu.DateUtilsBridge.*;
 
 public class DateIntervalFormatBenchmark extends SimpleBenchmark {
   public void timeDateIntervalFormat_formatDateRange_DATE(int reps) throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.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);
+      DateIntervalFormat.formatDateRange(l, utc, 0L, 0L, flags);
     }
   }
 
   public void timeDateIntervalFormat_formatDateRange_TIME(int reps) throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.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);
+      DateIntervalFormat.formatDateRange(l, utc, 0L, 0L, flags);
     }
   }
 
   public void timeDateIntervalFormat_formatDateRange_DATE_TIME(int reps) throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.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);
+      DateIntervalFormat.formatDateRange(l, utc, 0L, 0L, flags);
     }
   }
 }
diff --git a/benchmarks/src/benchmarks/regression/ReflectionBenchmark.java b/benchmarks/src/benchmarks/regression/ReflectionBenchmark.java
index f8f3c93..fcb2636 100644
--- a/benchmarks/src/benchmarks/regression/ReflectionBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/ReflectionBenchmark.java
@@ -156,6 +156,13 @@
         }
     }
 
+    public void timeClass_classNewInstance(int reps) throws Exception {
+        Class<?> klass = C.class;
+        for (int rep = 0; rep < reps; ++rep) {
+            klass.newInstance();
+        }
+    }
+
     public void timeGetInstanceField(int reps) throws Exception {
         for (int rep = 0; rep < reps; ++rep) {
             // The field here (and in timeGetStaticField) were chosen to be
diff --git a/benchmarks/src/benchmarks/regression/RelativeDateTimeFormatterBenchmark.java b/benchmarks/src/benchmarks/regression/RelativeDateTimeFormatterBenchmark.java
new file mode 100644
index 0000000..ea2cf4a
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/RelativeDateTimeFormatterBenchmark.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 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 benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeDateTimeString;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeTimeSpanString;
+
+public class RelativeDateTimeFormatterBenchmark extends SimpleBenchmark {
+  public void timeRelativeDateTimeFormatter_getRelativeTimeSpanString(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("Europe/London");
+    int flags = 0;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      getRelativeTimeSpanString(l, utc, 0L, 0L, 0L, flags);
+    }
+  }
+
+  public void timeRelativeDateTimeFormatter_getRelativeTimeSpanString_ABBREV(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = FORMAT_ABBREV_RELATIVE;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      getRelativeTimeSpanString(l, utc, 0L, 0L, 0L, flags);
+    }
+  }
+
+  public void timeRelativeDateTimeFormatter_getRelativeDateTimeString(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = 0;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      getRelativeDateTimeString(l, utc, 0L, 0L, 0L, 0L, flags);
+    }
+  }
+
+  public void timeRelativeDateTimeFormatter_getRelativeDateTimeString_ABBREV(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("America/Los_Angeles");
+    int flags = FORMAT_ABBREV_RELATIVE;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      getRelativeDateTimeString(l, utc, 0L, 0L, 0L, 0L, flags);
+    }
+  }
+}
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index 6a1a493..4124ffa 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -133,6 +133,23 @@
         return result.toString();
     }
 
+    /**
+     * Returns the list of jar/apk files containing classes and
+     * resources, delimited by {@code File.pathSeparator}.
+     *
+     * @hide
+     */
+    public String getDexPath() {
+        StringBuilder builder = new StringBuilder();
+        for (File file : pathList.getDexFiles()) {
+            if (builder.length() > 0) {
+                builder.append(':');
+            }
+            builder.append(file);
+        }
+        return builder.toString();
+    }
+
     @Override public String toString() {
         return getClass().getName() + "[" + pathList + "]";
     }
diff --git a/dalvik/src/main/java/dalvik/system/CloseGuard.java b/dalvik/src/main/java/dalvik/system/CloseGuard.java
index df36867..a45ffa1 100644
--- a/dalvik/src/main/java/dalvik/system/CloseGuard.java
+++ b/dalvik/src/main/java/dalvik/system/CloseGuard.java
@@ -40,6 +40,7 @@
  *
  *       protected void finalize() throws Throwable {
  *           try {
+ *               // Note that guard could be null if the constructor threw.
  *               if (guard != null) {
  *                   guard.warnIfOpen();
  *               }
@@ -76,6 +77,7 @@
  *
  *       protected void finalize() throws Throwable {
  *           try {
+ *               // Note that guard could be null if the constructor threw.
  *               if (guard != null) {
  *                   guard.warnIfOpen();
  *               }
@@ -94,12 +96,6 @@
  * in a method, the call to {@code open} should occur just after
  * resource acquisition.
  *
- * <p>
- *
- * Note that the null check on {@code guard} in the finalizer is to
- * cover cases where a constructor throws an exception causing the
- * {@code guard} to be uninitialized.
- *
  * @hide
  */
 public final class CloseGuard {
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 86bb531..79f2912 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -34,7 +34,7 @@
  * read-only by the VM.
  */
 public final class DexFile {
-    private long mCookie;
+    private Object mCookie;
     private final String mFileName;
     private final CloseGuard guard = CloseGuard.get();
 
@@ -175,10 +175,10 @@
      *             normally should not happen
      */
     public void close() throws IOException {
-        if (mCookie != 0) {
+        if (mCookie != null) {
             guard.close();
             closeDexFile(mCookie);
-            mCookie = 0;
+            mCookie = null;
         }
     }
 
@@ -219,7 +219,7 @@
         return defineClass(name, loader, mCookie, suppressed);
     }
 
-    private static Class defineClass(String name, ClassLoader loader, long cookie,
+    private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                      List<Throwable> suppressed) {
         Class result = null;
         try {
@@ -290,22 +290,22 @@
      * Open a DEX file.  The value returned is a magic VM cookie.  On
      * failure, an IOException is thrown.
      */
-    private static long openDexFile(String sourceName, String outputName, int flags) throws IOException {
+    private static Object openDexFile(String sourceName, String outputName, int flags) throws IOException {
         // Use absolute paths to enable the use of relative paths when testing on host.
         return openDexFileNative(new File(sourceName).getAbsolutePath(),
                                  (outputName == null) ? null : new File(outputName).getAbsolutePath(),
                                  flags);
     }
 
-    private static native void closeDexFile(long cookie);
-    private static native Class defineClassNative(String name, ClassLoader loader, long cookie)
+    private static native void closeDexFile(Object cookie);
+    private static native Class defineClassNative(String name, ClassLoader loader, Object cookie)
             throws ClassNotFoundException, NoClassDefFoundError;
-    private static native String[] getClassNameList(long cookie);
+    private static native String[] getClassNameList(Object cookie);
     /*
      * Open a DEX file.  The value returned is a magic VM cookie.  On
      * failure, an IOException is thrown.
      */
-    private static native long openDexFileNative(String sourceName, String outputName, int flags);
+    private static native Object openDexFileNative(String sourceName, String outputName, int flags);
 
     /**
      * Returns true if the VM believes that the apk/jar file is out of date
@@ -325,37 +325,44 @@
             throws FileNotFoundException, IOException;
 
     /**
-     * See {@link #isDexOptNeededInternal(String, String, String, boolean)}.
+     * See {@link #getDexOptNeeded(String, String, String, boolean)}.
      *
      * @hide
      */
-    public static final byte UP_TO_DATE = 0;
+    public static final int NO_DEXOPT_NEEDED = 0;
 
     /**
-     * See {@link #isDexOptNeededInternal(String, String, String, boolean)}.
+     * See {@link #getDexOptNeeded(String, String, String, boolean)}.
      *
      * @hide
      */
-    public static final byte PATCHOAT_NEEDED = 1;
+    public static final int DEX2OAT_NEEDED = 1;
 
     /**
-     * See {@link #isDexOptNeededInternal(String, String, String, boolean)}.
+     * See {@link #getDexOptNeeded(String, String, String, boolean)}.
      *
      * @hide
      */
-    public static final byte DEXOPT_NEEDED = 2;
+    public static final int PATCHOAT_NEEDED = 2;
 
     /**
-     * Returns UP_TO_DATE if the VM believes that the apk/jar file
-     * is up to date, PATCHOAT_NEEDED if it believes that the file is up
-     * to date but it must be relocated to match the base address offset,
-     * and DEXOPT_NEEDED if it believes that it is out of date and should
-     * be passed through "dexopt" again.
+     * See {@link #getDexOptNeeded(String, String, String, boolean)}.
+     *
+     * @hide
+     */
+    public static final int SELF_PATCHOAT_NEEDED = 3;
+
+    /**
+     * Returns the VM's opinion of what kind of dexopt is needed to make the
+     * apk/jar file up to date.
      *
      * @param fileName the absolute path to the apk/jar file to examine.
-     * @return DEXOPT_NEEDED if dexopt should be called on the file,
-     *         PATCHOAT_NEEDED if we need to run "patchoat" on it and
-     *         UP_TO_DATE otherwise.
+     * @return NO_DEXOPT_NEEDED if the apk/jar is already up to date.
+     *         DEX2OAT_NEEDED if dex2oat should be called on the apk/jar file.
+     *         PATCHOAT_NEEDED if patchoat should be called on the apk/jar
+     *         file to patch the odex file along side the apk/jar.
+     *         SELF_PATCHOAT_NEEDED if selfpatchoat should be called on the
+     *         apk/jar file to patch the oat file in the dalvik cache.
      * @throws java.io.FileNotFoundException if fileName is not readable,
      *         not a file, or not present.
      * @throws java.io.IOException if fileName is not a valid apk/jar file or
@@ -366,7 +373,7 @@
      *
      * @hide
      */
-    public static native byte isDexOptNeededInternal(String fileName, String pkgname,
+    public static native int getDexOptNeeded(String fileName, String pkgname,
             String instructionSet, boolean defer)
             throws FileNotFoundException, IOException;
 }
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index e364e40..b1ff1c8 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -51,6 +51,9 @@
     /** class definition context */
     private final ClassLoader definingContext;
 
+    /** List of dexfiles. */
+    private final List<File> dexFiles;
+
     /**
      * List of dex/resource (class path) elements.
      * Should be called pathElements, but the Facebook app uses reflection
@@ -59,7 +62,7 @@
     private final Element[] dexElements;
 
     /** List of native library directories. */
-    private final File[] nativeLibraryDirectories;
+    private final List<File> nativeLibraryDirectories;
 
     /**
      * Exceptions thrown during creation of the dexElements list.
@@ -106,7 +109,9 @@
 
         this.definingContext = definingContext;
         ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
-        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
+        // save dexPath for BaseDexClassLoader
+        this.dexFiles = splitDexPath(dexPath);
+        this.dexElements = makeDexElements(dexFiles, optimizedDirectory,
                                            suppressedExceptions);
         if (suppressedExceptions.size() > 0) {
             this.dexElementsSuppressedExceptions =
@@ -118,24 +123,34 @@
     }
 
     @Override public String toString() {
+        File[] nativeLibraryDirectoriesArray =
+                nativeLibraryDirectories.toArray(new File[nativeLibraryDirectories.size()]);
+
         return "DexPathList[" + Arrays.toString(dexElements) +
-            ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectories) + "]";
+            ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectoriesArray) + "]";
     }
 
     /**
      * For BaseDexClassLoader.getLdLibraryPath.
      */
-    public File[] getNativeLibraryDirectories() {
+    public List<File> getNativeLibraryDirectories() {
         return nativeLibraryDirectories;
     }
 
     /**
+     * For BaseDexClassLoader.getDexPath.
+     */
+    public List<File> getDexFiles() {
+        return dexFiles;
+    }
+
+    /**
      * Splits the given dex path string into elements using the path
      * separator, pruning out any elements that do not refer to existing
      * and readable files. (That is, directories are not included in the
      * result.)
      */
-    private static ArrayList<File> splitDexPath(String path) {
+    private static List<File> splitDexPath(String path) {
         return splitPaths(path, null, false);
     }
 
@@ -146,7 +161,7 @@
      * from the system library path, and pruning out any elements that
      * do not refer to existing and readable directories.
      */
-    private static File[] splitLibraryPath(String path) {
+    private static List<File> splitLibraryPath(String path) {
         // Native libraries may exist in both the system and
         // application library paths, and we use this search order:
         //
@@ -154,8 +169,7 @@
         //   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()]);
+        return splitPaths(path, System.getProperty("java.library.path"), true);
     }
 
     /**
@@ -167,9 +181,8 @@
      * are empty or {@code null}, or all elements get pruned out, then
      * this returns a zero-element list.
      */
-    private static ArrayList<File> splitPaths(String path1, String path2,
-            boolean wantDirectories) {
-        ArrayList<File> result = new ArrayList<File>();
+    private static List<File> splitPaths(String path1, String path2, boolean wantDirectories) {
+        List<File> result = new ArrayList<File>();
 
         splitAndAdd(path1, wantDirectories, result);
         splitAndAdd(path2, wantDirectories, result);
@@ -181,7 +194,7 @@
      * and filtering and adding to a result.
      */
     private static void splitAndAdd(String searchPath, boolean directoriesOnly,
-            ArrayList<File> resultList) {
+            List<File> resultList) {
         if (searchPath == null) {
             return;
         }
@@ -200,8 +213,8 @@
      * Makes an array of dex/resource path elements, one per element of
      * the given array.
      */
-    private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
-                                             ArrayList<IOException> suppressedExceptions) {
+    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
+                                             List<IOException> suppressedExceptions) {
         ArrayList<Element> elements = new ArrayList<Element>();
         /*
          * Open all files and load the (direct or contained) dex files
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index 4b606e6..59e28e2 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -18,6 +18,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Provides access to some VM-specific debug features. Though this class and
@@ -389,4 +391,57 @@
      * @param data the array into which the stats are written.
      */
     public static native void getHeapSpaceStats(long[] data);
+
+    /* Map from the names of the runtime stats supported by getRuntimeStat() to their IDs */
+    private static final HashMap<String, Integer> runtimeStatsMap = new HashMap<>();
+
+    static {
+        runtimeStatsMap.put("art.gc.gc-count", 0);
+        runtimeStatsMap.put("art.gc.gc-time", 1);
+        runtimeStatsMap.put("art.gc.bytes-allocated", 2);
+        runtimeStatsMap.put("art.gc.bytes-freed", 3);
+        runtimeStatsMap.put("art.gc.blocking-gc-count", 4);
+        runtimeStatsMap.put("art.gc.blocking-gc-time", 5);
+        runtimeStatsMap.put("art.gc.gc-count-rate-histogram", 6);
+        runtimeStatsMap.put("art.gc.blocking-gc-count-rate-histogram", 7);
+    }
+
+    /**
+     * Returns the value of a particular runtime statistic or {@code null} if no
+     * such runtime statistic exists.
+     *
+     * @param statName
+     *            the name of the runtime statistic to look up.
+     * @return the value of the runtime statistic.
+     */
+    public static String getRuntimeStat(String statName) {
+        if (statName == null) {
+            throw new NullPointerException("statName == null");
+        }
+        Integer statId = runtimeStatsMap.get(statName);
+        if (statId != null) {
+            return getRuntimeStatInternal(statId);
+        }
+        return null;
+    }
+
+    /**
+     * Returns a map of the names/values of the runtime statistics
+     * that {@link #getRuntimeStat()} supports.
+     *
+     * @return a map of the names/values of the supported runtime statistics.
+     */
+    public static Map<String, String> getRuntimeStats() {
+        HashMap<String, String> map = new HashMap<>();
+        String[] values = getRuntimeStatsInternal();
+        for (String name : runtimeStatsMap.keySet()) {
+            int id = runtimeStatsMap.get(name);
+            String value = values[id];
+            map.put(name, value);
+        }
+        return map;
+    }
+
+    private static native String getRuntimeStatInternal(int statId);
+    private static native String[] getRuntimeStatsInternal();
 }
diff --git a/dalvik/src/main/java/dalvik/system/ZygoteHooks.java b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
index 11ea286..134c2f4 100644
--- a/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
+++ b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
@@ -29,7 +29,7 @@
 
     /**
      * Called by the zygote prior to every fork. Each call to {@code preFork}
-     * is followed by a matching call to {@link #postForkChild(int)} on the child
+     * is followed by a matching call to {@link #postForkChild(int, String)} on the child
      * process and {@link #postForkCommon()} on both the parent and the child
      * process. {@code postForkCommon} is called after {@code postForkChild} in
      * the child process.
@@ -47,6 +47,8 @@
      */
     public void postForkChild(int debugFlags, String instructionSet) {
         nativePostForkChild(token, debugFlags, instructionSet);
+
+        Math.setRandomSeedInternal(System.currentTimeMillis());
     }
 
     /**
@@ -68,13 +70,10 @@
      */
     private static void waitUntilAllThreadsStopped() {
         File tasks = new File("/proc/self/task");
+        // All Java daemons are stopped already. We're just waiting for their OS counterparts to
+        // finish as well. This shouldn't take much time so spinning is ok here.
         while (tasks.list().length > 1) {
-            try {
-                // Experimentally, booting and playing about with a stingray, I never saw us
-                // go round this loop more than once with a 10ms sleep.
-                Thread.sleep(10);
-            } catch (InterruptedException ignored) {
-            }
+          Thread.yield();
         }
     }
 }
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index aa06985..0a047a2 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -67,7 +67,6 @@
   result: EXEC_FAILED,
   names: [
     "org.apache.harmony.tests.java.net.Inet6AddressTest#test_getByNameLjava_lang_String",
-    "org.apache.harmony.tests.java.net.InetAddressTest#test_equalsLjava_lang_Object",
     "org.apache.harmony.tests.java.net.InetAddressTest#test_getByNameLjava_lang_String",
     "org.apache.harmony.tests.java.net.InetAddressTest#test_isReachableLjava_net_NetworkInterfaceII_loopbackInterface"
   ]
@@ -91,8 +90,8 @@
 },
 {
   description: "The ResourceBundle code under test is probably not used much on Android and needs a lot of attention.",
+  modes: [device],
   bug: 13747957,
-  result: EXEC_FAILED,
   names: [
       "org.apache.harmony.tests.java.util.ControlTest#test_needsReload_LStringLLocaleLStringLClassLoaderResourceBundleJ"
   ]
@@ -100,6 +99,7 @@
 {
   description: "Fails in CTS, passes in CoreTestRunner.",
   result: EXEC_FAILED,
+  modes: [device],
   names: [
       "org.apache.harmony.tests.java.net.URLConnection#test_getContentEncoding",
       "libcore.java.text.OldNumberFormatTest#test_parseLjava_lang_String",
@@ -151,7 +151,7 @@
 },
 {
   description: "Causes open dex file error",
-  result: EXEC_FAILED,
+  modes: [device],
   names: [
       "org.apache.harmony.tests.java.lang.reflect.GenericSignatureFormatErrorTest#test_signatureFormatError"
   ]
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index d14afdc..6470e36 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -4,6 +4,7 @@
 [
 {
   description: "can't compile a pattern with negative look-behind and quantifiers with upper bounds",
+  result: EXEC_FAILED,
   name: "org.apache.harmony.regex.tests.java.util.regex.PatternTest#test_bug_40103",
   bug: 40103
 },
@@ -76,6 +77,7 @@
 {
   description: "Runtime.getRuntime().traceMethodCalls(true) doesn't return on the host, fails in CTS",
   bug: 3447964,
+  result: EXEC_FAILED,
   name: "libcore.java.lang.OldRuntimeTest#test_traceMethodCalls"
 },
 {
@@ -1317,11 +1319,13 @@
 {
   description: "Known failure in MathTest 1^NAN should be NAN",
   bug: 11669804,
+  result: EXEC_FAILED,
   name: "org.apache.harmony.tests.java.lang.MathTest#test_powDD"
 },
 {
   description: "Known failures in PropertiesTest: We don't deal with comments in store()",
   bug: 11686302,
+  result: EXEC_FAILED,
   names: [
     "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario0",
     "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario1",
@@ -1343,6 +1347,7 @@
 {
   description: "ScannerParseLargeFileBenchmark can cause a failure due to a timeout",
   bug: 14865710,
+  result: EXEC_FAILED,
   name: "org.apache.harmony.tests.java.util.ScannerParseLargeFileBenchmarkTest"
 },
 {
@@ -1419,14 +1424,6 @@
   name: "com.android.org.apache.harmony.logging.tests.java.util.logging.LevelTest#testSerializationCompatibility"
 },
 {
-  description: "java.util.logging: Tests that require java.util.logging code changes to fix.",
-  bug: 13882147,
-  names: [
-    "com.android.org.apache.harmony.logging.tests.java.util.logging.SocketHandlerTest#testConstructor_NoProperties",
-    "com.android.org.apache.harmony.logging.tests.java.util.logging.XMLFormatterTest#test_TestFileHandlerClass_constructor"
-  ]
-},
-{
   description: "java.util.beans: the harmony tests were broken by Android commit 19a270e90b1e992c1f6639f355ae13564c2f3a6a",
   bug: 17394106,
   names: [
@@ -1457,47 +1454,11 @@
   name: "org.apache.harmony.tests.java.util.GregorianCalendarTest#test_computeTime"
 },
 {
-  description: "SpdyConnection issue https://github.com/square/okhttp/issues/644 crashes the test app. Android does not provide SPDY/HTTP_2 connections by default so have been suppressed.",
-  bug: 14462336,
+  description: "OkHttp tests require SOCKS 5 support. Android PlainSocketImpl implements SOCKS 4",
+  bug: 96926,
   names: [
-    "com.squareup.okhttp.ConnectionPoolTest",
-    "com.squareup.okhttp.internal.spdy.SpdyConnectionTest",
-    "com.squareup.okhttp.internal.http.HttpOverHttp20Draft09Test",
-    "com.squareup.okhttp.internal.http.HttpOverSpdy3Test",
-    "com.squareup.okhttp.internal.http.ResponseCacheAdapterTest",
-    "com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_SPDY_3",
-    "com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_HTTP_2",
-    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPost_SPDY_3",
-    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPost_HTTP_2",
-    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPut_SPDY_3",
-    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPut_HTTP_2"
-  ]
-},
-{
-  description: "Some OkHttp tests were written before the introduction of TLS_FALLBACK_SCSV and have only been fixed for APIs used by Android",
-  bug: 17962997,
-  names: [
-    "com.squareup.okhttp.SyncApiTest#recoverFromTlsHandshakeFailure",
-    "com.squareup.okhttp.AsyncApiTest#recoverFromTlsHandshakeFailure"
-  ]
-},
-{
-  description: "JavaApiConverterTest#createOkResponse_fromJavaHttpsUrlConnection works independently but fails when run with some other test(s).",
-  bug: 17962997,
-  name: "com.squareup.okhttp.internal.http.JavaApiConverterTest#createOkResponse_fromJavaHttpsUrlConnection"
-},
-{
-  description: "Okhttp test hardcodes the TLS version expected.",
-  bug: 14462336,
-  names: [
-    "com.squareup.okhttp.internal.http.URLConnectionTest#sslFallbackNotUsedWhenRecycledConnectionFails"
-  ]
-},
-{
-  description: "The test relies on SimpleDateFormat zzz producing GMT not GMT+00:00 as it does on Android. Android issue 66136.",
-  bug: 14462336,
-  names: [
-    "com.squareup.okhttp.internal.http.HttpResponseCacheTest#setIfModifiedSince"
+    "com.squareup.okhttp.SocksProxyTest#proxy",
+    "com.squareup.okhttp.SocksProxyTest#proxySelector"
   ]
 },
 {
@@ -1514,5 +1475,39 @@
   names: [
     "libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale"
   ]
+},
+{
+  description: "Tests failing on host - needing investigation.",
+  modes: [host],
+  bug: 18547404,
+  result: EXEC_FAILED,
+  names: [
+    "org.apache.harmony.tests.java.util.HashMapTest#test_EntrySet"
+  ]
+},
+{
+  description: "Differences between glibc and bionic",
+  modes_variants: [[host, X64]],
+  bug: 18087920,
+  result: EXEC_FAILED,
+  names: [
+    "org.apache.harmony.tests.java.lang.MathTest#test_cbrt_D"
+  ]
+},
+{
+  description: "Recursive calls to Charset.forName from within providers will overflow the stack.",
+  result: EXEC_FAILED,
+  names: [
+    "org.apache.harmony.tests.java.nio.charset.CharsetTest#testForName_withProviderWithRecursiveCall"
+  ]
+},
+{
+  description: "Zip64 tests take too long to execute and need more than 5GB of space to run.",
+  result: EXEC_FAILED,
+  names: [
+    "libcore.java.util.zip.Zip64FileTest#testZip64Support_largeNumberOfEntries",
+    "libcore.java.util.zip.Zip64FileTest#testZip64Support_totalLargerThan4G",
+    "libcore.java.util.zip.Zip64FileTest#testZip64Support_hugeEntry"
+  ]
 }
 ]
diff --git a/expectations/taggedtests.txt b/expectations/taggedtests.txt
index aa220d0..cc2a4d6 100644
--- a/expectations/taggedtests.txt
+++ b/expectations/taggedtests.txt
@@ -37,6 +37,7 @@
     "java.util.zip.LargeZip",
     "java.util.zip.ZipFile.LargeZipFile",
     "java.util.zip.ZipFile.ManyEntries",
+    "libcore.java.lang.SystemTest_testArrayCopyConcurrentModification",
     "sun.nio.cs.FindOneCharEncoderBugs",
     "sun.nio.cs.SurrogateGB18030Test"
   ],
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
index 5cc88f4..77812ed 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
@@ -227,8 +227,8 @@
             assertEquals("wrong result 4", ref1.getPath(), file4.getPath());
         }
 
-        File ref2 = new File("/lib/content-types.properties");
-        File file5 = new File("/", "lib/content-types.properties");
+        File ref2 = new File("/lib/test_112270.properties");
+        File file5 = new File("/", "lib/test_112270.properties");
         assertEquals("wrong result 5", ref2.getPath(), file5.getPath());
     }
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java
index b87105b..c7a6e71 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ProcessTest.java
@@ -22,9 +22,10 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import libcore.io.Libcore;
 
 public class ProcessTest extends junit.framework.TestCase {
-
+  // Test that failures to exec don't leave zombies lying around.
   public void test_55017() throws Exception {
     ArrayList<Process> children = new ArrayList<Process>();
     for (int i = 0; i < 256; ++i) {
@@ -36,13 +37,15 @@
     }
     assertEquals(0, children.size());
 
+    String pid = Integer.toString(Libcore.os.getpid());
     boolean onDevice = new File("/system/bin").exists();
-    String[] psCommand = onDevice ? new String[] { "ps" } : new String[] { "ps", "s" };
+    String[] psCommand = onDevice ? new String[] { "ps", "--ppid", pid }
+                                  : new String[] { "ps", "S", "--ppid", pid };
     Process ps = Runtime.getRuntime().exec(psCommand, null, null);
     int zombieCount = 0;
     for (String line : readAndCloseStream(ps.getInputStream()).split("\n")) {
       if (line.contains(" Z ") || line.contains(" Z+ ")) {
-          ++zombieCount;
+        ++zombieCount;
       }
     }
     assertEquals(0, zombieCount);
@@ -86,7 +89,7 @@
     assertEquals("", received);
 
     String stderr = readAndCloseStream(p.getErrorStream());
-    assertTrue(stderr, stderr.contains("unrecognized option") || stderr.contains("invalid option"));
+    assertTrue(stderr, stderr.contains("no-such-option"));
 
     p.waitFor();
     p.destroy();
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceQueueTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceQueueTest.java
index 75a5218..e010116 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceQueueTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceQueueTest.java
@@ -22,6 +22,7 @@
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
+import java.util.concurrent.CountDownLatch;
 import libcore.java.lang.ref.FinalizationTester;
 
 public class ReferenceQueueTest extends junit.framework.TestCase {
@@ -198,27 +199,29 @@
         assertNull(rq.poll());
 
         class RemoveThread extends Thread {
+            public final CountDownLatch inBlock = new CountDownLatch(1);
+            public final CountDownLatch outOfBlock = new CountDownLatch(1);
             public void run() {
                 try {
+                    inBlock.countDown();
                     rq.remove(1000L);
                 } catch(InterruptedException ie) {
                     isThrown = true;
                 }
+                outOfBlock.countDown();
             }
         }
         RemoveThread rt = new RemoveThread();
         rt.start();
         try {
+            rt.inBlock.await();
+            // Try to be inside of rq.remove(1000L) if possible.
             Thread.sleep(10);
-        } catch(InterruptedException ie) {
-
-        }
+        } catch(InterruptedException ie) {}
         rt.interrupt();
         try {
-            Thread.sleep(10);
-        } catch(InterruptedException ie) {
-
-        }
+            rt.outOfBlock.await();
+        } catch(InterruptedException ie) {}
         assertTrue(isThrown);
         assertNull(rq.poll());
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java
index e585b14..d9f3d91 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/DatagramSocketTest.java
@@ -745,12 +745,13 @@
 
     public void test_getLocalSocketAddress_ANY() throws Exception {
         DatagramSocket s = new DatagramSocket(0);
-        try {
-            assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(),
-                    ((InetSocketAddress) s.getLocalSocketAddress()).getAddress() instanceof Inet6Address);
-        } finally {
-            s.close();
-        }
+        assertEquals("ANY address not IPv6: " + s.getLocalSocketAddress(),
+                Inet6Address.ANY, s.getLocalAddress());
+        s.close();
+        s = new DatagramSocket(0, null);
+        assertEquals(Inet6Address.ANY, s.getLocalAddress());
+        assertFalse(0 == s.getLocalPort());
+        s.close();
     }
 
     public void test_setReuseAddressZ() throws Exception {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
index 42f88c1..e5742d7 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
@@ -47,7 +47,7 @@
     }
 
     public void test_equalsLjava_lang_Object() throws Exception {
-        InetAddress ia1 = InetAddress.getByName("localhost");
+        InetAddress ia1 = InetAddress.getByName("ip6-localhost");
         InetAddress ia2 = InetAddress.getByName("::1");
         assertEquals(ia2, ia1);
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
index d531301..b6a5861 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
@@ -49,7 +49,6 @@
 
     private static InetAddress GOOD_IPv4 = lookup("224.0.0.3");
     private static InetAddress BAD_IPv4 = lookup("224.0.0.4");
-
     private static InetAddress GOOD_IPv6 = lookup("ff05::7:7");
     private static InetAddress BAD_IPv6 = lookup("ff05::7:8");
 
@@ -87,14 +86,15 @@
                     final InetAddress nextAddress = addresses.nextElement();
                     if (nextAddress instanceof Inet6Address && ipv6NetworkInterface == null) {
                         ipv6NetworkInterface = nextInterface;
-                    } else if (nextAddress instanceof Inet4Address && ipv4NetworkInterface == null) {
+                    } else if (nextAddress instanceof Inet4Address
+                            && ipv4NetworkInterface == null) {
                         ipv4NetworkInterface = nextInterface;
                     }
                 }
             }
         }
         assertTrue("Test environment must have at least one interface capable of multicast for IPv4"
-                + " and IPv6",
+                        + " and IPv6",
                 ipv4NetworkInterface != null && ipv6NetworkInterface != null);
     }
 
@@ -309,18 +309,31 @@
                 ipv6NetworkInterface, GOOD_IPv6, BAD_IPv6);
     }
 
+    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4_nullInterface()
+            throws Exception {
+        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(null, GOOD_IPv4, BAD_IPv4);
+    }
+
+    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6_nullInterface()
+            throws Exception {
+        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(null, GOOD_IPv6, BAD_IPv6);
+    }
+
     private void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
             NetworkInterface networkInterface, InetAddress group, InetAddress group2)
             throws Exception {
-        // Check that we can join a group using a null network interface.
+        // Create the sending socket and specify the interface to use as needed (otherwise use the
+        // default).
         MulticastSocket sendingSocket = new MulticastSocket(0);
-        SocketAddress groupSockAddr = new InetSocketAddress(group, sendingSocket.getLocalPort());
-        sendingSocket.joinGroup(groupSockAddr, null);
+        if (networkInterface != null) {
+            sendingSocket.setNetworkInterface(networkInterface);
+        }
         sendingSocket.setTimeToLive(2);
 
         MulticastSocket receivingSocket = createReceivingSocket(0);
         InetSocketAddress groupAddress =
                 new InetSocketAddress(group, receivingSocket.getLocalPort());
+        // Join the group. A null network interface is valid and means "use default".
         receivingSocket.joinGroup(groupAddress, networkInterface);
 
         String msg = "Hello World";
@@ -334,12 +347,19 @@
         receivingSocket.close();
         sendingSocket.close();
 
+        // Create the sending socket and specify the interface to use as needed (otherwise use the
+        // default).
+        sendingSocket = new MulticastSocket(0);
+        if (networkInterface != null) {
+            sendingSocket.setNetworkInterface(networkInterface);
+        }
+        sendingSocket.setTimeToLive(10);
+
         receivingSocket = createReceivingSocket(0);
         groupAddress = new InetSocketAddress(group, receivingSocket.getLocalPort());
+        // Join the group. A null network interface is valid and means "use default".
         receivingSocket.joinGroup(groupAddress, networkInterface);
 
-        sendingSocket = new MulticastSocket(0);
-        sendingSocket.setTimeToLive(10);
         msg = "Hello World - Different Group";
         InetSocketAddress group2Address =
                 new InetSocketAddress(group2, receivingSocket.getLocalPort());
@@ -706,7 +726,7 @@
         Enumeration theInterfaces = NetworkInterface.getNetworkInterfaces();
         while (theInterfaces.hasMoreElements()) {
             NetworkInterface thisInterface = (NetworkInterface) theInterfaces.nextElement();
-            if (thisInterface.getInetAddresses().hasMoreElements() && thisInterface.isUp()) {
+            if (willWorkForMulticast(thisInterface)) {
                 if ((!(thisInterface.getInetAddresses().nextElement()).isLoopbackAddress())) {
                     MulticastSocket receivingSocket = createReceivingSocket(0);
                     InetSocketAddress groupAddress =
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java
index 01cf40e..2cf278d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/charset/CharsetTest.java
@@ -25,15 +25,14 @@
 import java.nio.charset.IllegalCharsetNameException;
 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;
+import libcore.java.nio.charset.SettableCharsetProvider;
 
 import junit.framework.TestCase;
 
@@ -819,51 +818,94 @@
 
   // 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"));
+    SettableCharsetProvider.setDelegate(new MockCharsetProvider());
+    try {
+      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") 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);
+      assertTrue(Charset.forName("mockCharset10") == charset2);
+      // intended case problem in mock
+      Charset.forName("mockCharset11");
+      assertTrue(Charset.forName("mockCharset12") == charset2);
+    } finally {
+      SettableCharsetProvider.clearDelegate();
+    }
   }
 
   // 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"));
+    SettableCharsetProvider.setDelegate(new MockCharsetProvider());
+    try {
+      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().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"));
+      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 {
+      SettableCharsetProvider.clearDelegate();
+    }
   }
 
   // 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);
+    SettableCharsetProvider.setDelegate(new MockCharsetProviderASCII());
+    try {
+      assertFalse(Charset.forName("us-ascii") instanceof MockCharset);
+      assertFalse(Charset.availableCharsets().get("us-ascii") instanceof MockCharset);
+    } finally {
+      SettableCharsetProvider.clearDelegate();
+    }
+  }
+
+  // Fails on Android with a StackOverflowException.
+  public void testForName_withProviderWithRecursiveCall() throws Exception {
+    SettableCharsetProvider.setDelegate(new MockCharsetProviderWithRecursiveCall());
+    try {
+      Charset.forName("poop");
+      fail();
+    } catch (UnsupportedCharsetException expected) {
+    } finally {
+      SettableCharsetProvider.clearDelegate();
+    }
+  }
+
+  public static class MockCharsetProviderWithRecursiveCall extends CharsetProvider {
+      @Override
+      public Iterator<Charset> charsets() {
+          return null;
+      }
+
+      @Override
+      public Charset charsetForName(String charsetName) {
+          if (Charset.isSupported(charsetName)) {
+              return Charset.forName(charsetName);
+          }
+
+          return null;
+      }
   }
 
   public static class MockCharsetProvider extends CharsetProvider {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java
index e4d6bd8..d52e586 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/ChoiceFormatTest.java
@@ -21,6 +21,7 @@
 import java.text.FieldPosition;
 import java.text.MessageFormat;
 import java.text.ParsePosition;
+import java.util.Locale;
 
 import junit.framework.TestCase;
 
@@ -466,4 +467,23 @@
 		assertEquals("GREATER_THAN_ONE", fmt.format(999999999D));
 		assertEquals("GREATER_THAN_ONE", fmt.format(Double.POSITIVE_INFINITY));
 	}
+
+    // http://b/19149384
+    public void testToPatternWithInfinities() {
+        final ChoiceFormat fmt = new ChoiceFormat(
+                "-\u221E<are negative|0<are fractions|1#is one|1.0<is 1+|\u221E<are many.");
+        assertEquals("-\u221E<are negative|0.0<are fractions|1.0#is one|1.0<is 1+|\u221E<are many.",
+                fmt.toPattern());
+    }
+
+    // http://b/19011159
+    public void testEscapedPatternWithConsecutiveQuotes() {
+        ChoiceFormat format = new ChoiceFormat("0#1'2''3'''4''''.");
+        String formatted = format.format(0);
+        assertEquals("12'3'4''.", formatted);
+
+        format = new ChoiceFormat("0#1'2''3'''''4''''.");
+        formatted = format.format(0);
+        assertEquals("12'3''4''.", formatted);
+    }
 }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/MessageFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/MessageFormatTest.java
index 171f247..0920714 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/MessageFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/MessageFormatTest.java
@@ -950,4 +950,12 @@
     String res = MessageFormat.format("bgcolor=\"{10}\"", messageArgs);
     assertEquals(res, "bgcolor=\"example10\"");
   }
+
+  // http://b/19011159
+  public void test19011159() {
+    final String pattern = "ab{0,choice,0#1'2''3'''4''''.}yz";
+    final MessageFormat format = new MessageFormat(pattern, Locale.ENGLISH);
+    final Object[] zero0 = new Object[] { 0 };
+    assertEquals("ab12'3'4''.yz", format.format(zero0));
+  }
 }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java
index 07256e4..a32845c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java
@@ -154,7 +154,7 @@
         // BEGIN android-changed
         assertEquals("currK.getSymbol()", "\u20a9", currK.getSymbol());
         assertEquals("currI.getSymbol()", "IEP", currI.getSymbol());
-        assertEquals("currUS.getSymbol()", "$", currUS.getSymbol());
+        assertEquals("currUS.getSymbol()", "US$", currUS.getSymbol());
         // END android-changed
 
         // Test what happens if the default is an invalid locale, one with the country Korea (KR)
@@ -220,7 +220,7 @@
         // \u00a5 and \uffe5 are actually the same symbol, just different code points.
         // But the RI returns the \uffe5 and Android returns those with \u00a5
         String[] yen     = new String[] {"JPY", "\u00a5", "\u00a5JP", "JP\u00a5", "\uffe5", "\uffe5JP", "JP\uffe5"};
-        String[] dollar  = new String[] {"USD", "$", "US$", "$US"};
+        String[] dollar  = new String[] {"USD", "$", "US$", "$US", "$ US"};
         // BEGIN android-changed
         // Starting CLDR 1.7 release, currency symbol for CAD changed to CA$ in some locales such as ja.
         String[] cDollar = new String[] {"CA$", "CAD", "$", "Can$", "$CA"};
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumMapTest.java
index 2a37a18..676e373 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/EnumMapTest.java
@@ -233,6 +233,17 @@
         }
     }
 
+    @SuppressWarnings("unchecked")
+    public void testConstructor_badMapArg() {
+        HashMap badMap = new HashMap();
+        badMap.put("NotAnEnum", "Value");
+        try {
+            new EnumMap<Color, String>(badMap);
+            fail();
+        } catch (ClassCastException expected) {
+        }
+    }
+
     /**
      * java.util.EnumMap#clear()
      */
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ServerSocketFactoryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ServerSocketFactoryTest.java
index 34d7aed..ae4236c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ServerSocketFactoryTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ServerSocketFactoryTest.java
@@ -20,7 +20,7 @@
 * @version $Revision$
 */
 
-package tests.api.javax.net;
+package org.apache.harmony.tests.javax.net;
 
 import java.io.IOException;
 import java.net.InetAddress;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
index d566ee3..649be09 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
@@ -20,7 +20,7 @@
 * @version $Revision$
 */
 
-package tests.api.javax.net;
+package org.apache.harmony.tests.javax.net;
 
 import java.io.IOException;
 import java.net.InetAddress;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertPathTrustManagerParametersTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertPathTrustManagerParametersTest.java
index 41407e8..0951bce 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertPathTrustManagerParametersTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertPathTrustManagerParametersTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.security.cert.CertPathParameters;
 import javax.net.ssl.CertPathTrustManagerParameters;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertificatesToPlayWith.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertificatesToPlayWith.java
index 5bb6f06..9e2ea8d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertificatesToPlayWith.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/CertificatesToPlayWith.java
@@ -29,7 +29,7 @@
  *
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 /**
  * Some X509 certificates to test against.
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java
index c075cea..ec2f4f31 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import dalvik.annotation.AndroidOnly;
 import java.io.ByteArrayInputStream;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java
index 839f3b5..013c49b 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HostnameVerifierTest.java
@@ -15,7 +15,7 @@
  * the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -107,7 +107,7 @@
         in = new ByteArrayInputStream(X509_WILD_FOO);
         x509 = (X509Certificate) cf.generateCertificate(in);
         session = new mySSLSession(new X509Certificate[] {x509});
-        assertTrue(verifier.verify("foo.com", session));
+        assertFalse(verifier.verify("foo.com", session));
         assertTrue(verifier.verify("www.foo.com", session));
         assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session));
         assertFalse(verifier.verify("a.b.foo.com", session));
@@ -122,7 +122,7 @@
         x509 = (X509Certificate) cf.generateCertificate(in);
         session = new mySSLSession(new X509Certificate[] {x509});
         // try the foo.com variations
-        assertTrue(verifier.verify("foo.com", session));
+        assertFalse(verifier.verify("foo.com", session));
         assertTrue(verifier.verify("www.foo.com", session));
         assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session));
         assertFalse(verifier.verify("a.b.foo.com", session));
@@ -282,7 +282,7 @@
         assertFalse(verifier.verify("foo.com", session));
         assertTrue(verifier.verify("bar.com", session));
         assertTrue(verifier.verify("a.baz.com", session));
-        assertTrue(verifier.verify("baz.com", session));
+        assertFalse(verifier.verify("baz.com", session));
         assertFalse(verifier.verify("a.foo.com", session));
         assertFalse(verifier.verify("a.bar.com", session));
         assertFalse(verifier.verify("quux.com", session));
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java
index e2ebdbd..f2705ea 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HttpsURLConnectionTest.java
@@ -16,7 +16,7 @@
  */
 
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.ByteArrayInputStream;
 import java.net.URL;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory1Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory1Test.java
index b369ed9..a2d1a18 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory1Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory1Test.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.IOException;
 import java.security.InvalidAlgorithmParameterException;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory2Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory2Test.java
index 3d44396..006d0df 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory2Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactory2Test.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyStore;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactorySpiTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactorySpiTest.java
index fd44fda..fc98cbe 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactorySpiTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyManagerFactorySpiTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyStore;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyStoreBuilderParametersTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyStoreBuilderParametersTest.java
index a2fe49c..84fecd3 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyStoreBuilderParametersTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/KeyStoreBuilderParametersTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.KeyManagerFactorySpi;
 import javax.net.ssl.KeyStoreBuilderParameters;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext1Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext1Test.java
index ee51cc0..96168b0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext1Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext1Test.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.FileNotFoundException;
 import java.security.KeyManagementException;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext2Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext2Test.java
index 0881615..b35843d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext2Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContext2Test.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.security.KeyManagementException;
 import java.security.NoSuchAlgorithmException;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContextSpiTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContextSpiTest.java
index 45a1c14..45e9d40 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContextSpiTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLContextSpiTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContextSpi;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultHandshakeStatusTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultHandshakeStatusTest.java
index 3b13673..e0e2137 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultHandshakeStatusTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultHandshakeStatusTest.java
@@ -14,7 +14,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLEngineResult;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultStatusTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultStatusTest.java
index 05c8f03..906b8b0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultStatusTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultStatusTest.java
@@ -14,7 +14,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLEngineResult;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultTest.java
index 7a2ac78..fd1d389 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineResultTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLEngineResult;
 import junit.framework.TestCase;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java
index 31f5881..8f68d6e 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLEngineTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLExceptionTest.java
index 9b4ae35..addffd5 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLExceptionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLExceptionTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLException;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLHandshakeExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLHandshakeExceptionTest.java
index a2d0ec5a..c6770ea 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLHandshakeExceptionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLHandshakeExceptionTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLHandshakeException;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLKeyExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLKeyExceptionTest.java
index d6ba998..961dcb2 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLKeyExceptionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLKeyExceptionTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLKeyException;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLPeerUnverifiedExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLPeerUnverifiedExceptionTest.java
index a8b9ac4..fd74ce2 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLPeerUnverifiedExceptionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLPeerUnverifiedExceptionTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLPeerUnverifiedException;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLProtocolExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLProtocolExceptionTest.java
index 14b562d..6763ec7 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLProtocolExceptionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLProtocolExceptionTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLProtocolException;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketFactoryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketFactoryTest.java
index 5958ecb..ea291ad 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketFactoryTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketFactoryTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.IOException;
 import java.net.InetAddress;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java
index 5086f65..5a0cf6f 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLServerSocketTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import junit.framework.TestCase;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingEventTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingEventTest.java
index dfccf68..ed0944d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingEventTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingEventTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.security.Principal;
 import java.security.cert.Certificate;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingListenerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingListenerTest.java
index a006a83..6c597b3 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingListenerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionBindingListenerTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLServerSocketFactory;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionContextTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionContextTest.java
index aa95eb8..c0beb99 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionContextTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionContextTest.java
@@ -1,4 +1,4 @@
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import junit.framework.TestCase;
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java
index 2ba913d..0c0b384 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSessionTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import libcore.java.security.StandardNames;
 import java.io.ByteArrayInputStream;
@@ -38,8 +38,8 @@
 import javax.net.ssl.TrustManager;
 import junit.framework.TestCase;
 import libcore.io.Base64;
-import tests.api.javax.net.ssl.HandshakeCompletedEventTest.MyHandshakeListener;
-import tests.api.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
+import org.apache.harmony.tests.javax.net.ssl.HandshakeCompletedEventTest.MyHandshakeListener;
+import org.apache.harmony.tests.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
 
 public class SSLSessionTest extends TestCase {
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketFactoryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketFactoryTest.java
index e890032..2f456e0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketFactoryTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.IOException;
 import java.net.ServerSocket;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java
index 950d534..11e3142 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/SSLSocketTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import dalvik.annotation.AndroidOnly;
 import java.io.ByteArrayInputStream;
@@ -37,8 +37,8 @@
 import javax.security.cert.X509Certificate;
 import junit.framework.TestCase;
 import libcore.io.Base64;
-import tests.api.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
 import libcore.java.security.StandardNames;
+import org.apache.harmony.tests.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
 
 public class SSLSocketTest extends TestCase {
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java
index 23b24e4..9b5b929 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory1Test.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import dalvik.annotation.KnownFailure;
 import java.io.IOException;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory2Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory2Test.java
index 0c0760f..e495419 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory2Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactory2Test.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyStore;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactorySpiTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactorySpiTest.java
index 7bdeb45..7d15787 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactorySpiTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/TrustManagerFactorySpiTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.security.AccessController;
 import java.security.InvalidAlgorithmParameterException;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509ExtendedKeyManagerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509ExtendedKeyManagerTest.java
index 514fed8..13740af 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509ExtendedKeyManagerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509ExtendedKeyManagerTest.java
@@ -15,7 +15,7 @@
  *  limitations under the License.
  */
 
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.net.Socket;
 import java.security.Principal;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509KeyManagerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509KeyManagerTest.java
index 4dddbc6..6ed67bd 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509KeyManagerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509KeyManagerTest.java
@@ -1,4 +1,4 @@
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.ByteArrayInputStream;
 import java.net.Socket;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509TrustManagerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509TrustManagerTest.java
index ade0eca..ece0a73 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509TrustManagerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/X509TrustManagerTest.java
@@ -1,4 +1,4 @@
-package tests.api.javax.net.ssl;
+package org.apache.harmony.tests.javax.net.ssl;
 
 import java.io.ByteArrayInputStream;
 import java.security.cert.CertificateException;
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/cert/X509CertificateTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/cert/X509CertificateTest.java
index 64bfbb3..26403f5 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/cert/X509CertificateTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/security/cert/X509CertificateTest.java
@@ -20,7 +20,7 @@
  * @version $Revision$
  */
 
-package tests.api.javax.security.cert;
+package org.apache.harmony.tests.javax.security.cert;
 
 import junit.framework.Test;
 import junit.framework.TestCase;
diff --git a/harmony-tests/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider b/harmony-tests/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
deleted file mode 100644
index 9cc124a..0000000
--- a/harmony-tests/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
+++ /dev/null
@@ -1,2 +0,0 @@
-org.apache.harmony.tests.java.nio.charset.CharsetTest$MockCharsetProvider
-org.apache.harmony.tests.java.nio.charset.CharsetTest$MockCharsetProviderASCII
diff --git a/include/ScopedIcuLocale.h b/include/ScopedIcuLocale.h
index 2109e03..64186e3 100644
--- a/include/ScopedIcuLocale.h
+++ b/include/ScopedIcuLocale.h
@@ -36,7 +36,7 @@
       return;
     }
 
-    mLocale = Locale::createFromName(localeName.c_str());
+    mLocale = icu::Locale::createFromName(localeName.c_str());
   }
 
   ~ScopedIcuLocale() {
@@ -46,13 +46,13 @@
     return !mLocale.isBogus();
   }
 
-  Locale& locale() {
+  icu::Locale& locale() {
     return mLocale;
   }
 
  private:
   JNIEnv* const mEnv;
-  Locale mLocale;
+  icu::Locale mLocale;
 
   // Disallow copy and assignment.
   ScopedIcuLocale(const ScopedIcuLocale&);
diff --git a/include/ScopedJavaUnicodeString.h b/include/ScopedJavaUnicodeString.h
index f6ed7ad..c1f487e 100644
--- a/include/ScopedJavaUnicodeString.h
+++ b/include/ScopedJavaUnicodeString.h
@@ -45,11 +45,11 @@
     return (mString != NULL);
   }
 
-  const UnicodeString& unicodeString() const {
+  const icu::UnicodeString& unicodeString() const {
     return mUnicodeString;
   }
 
-  UnicodeString& unicodeString() {
+  icu::UnicodeString& unicodeString() {
     return mUnicodeString;
   }
 
@@ -57,7 +57,7 @@
   JNIEnv* mEnv;
   jstring mString;
   const UChar* mChars;
-  UnicodeString mUnicodeString;
+  icu::UnicodeString mUnicodeString;
 
   // Disallow copy and assignment.
   ScopedJavaUnicodeString(const ScopedJavaUnicodeString&);
diff --git a/jarjar-rules.txt b/jarjar-rules.txt
new file mode 100644
index 0000000..b2da6eb
--- /dev/null
+++ b/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.ibm.icu.** android.icu.@1
diff --git a/json/src/main/java/org/json/JSONTokener.java b/json/src/main/java/org/json/JSONTokener.java
index 202e2e6..8caecc8 100644
--- a/json/src/main/java/org/json/JSONTokener.java
+++ b/json/src/main/java/org/json/JSONTokener.java
@@ -188,8 +188,6 @@
      * not include it in the returned string.
      *
      * @param quote either ' or ".
-     * @throws NumberFormatException if any unicode escape sequences are
-     *     malformed.
      */
     public String nextString(char quote) throws JSONException {
         /*
@@ -235,9 +233,6 @@
      * immediately follow a backslash. The backslash '\' should have already
      * been read. This supports both unicode escapes "u000A" and two-character
      * escapes "\n".
-     *
-     * @throws NumberFormatException if any unicode escape sequences are
-     *     malformed.
      */
     private char readEscapeCharacter() throws JSONException {
         char escaped = in.charAt(pos++);
@@ -248,7 +243,11 @@
                 }
                 String hex = in.substring(pos, pos + 4);
                 pos += 4;
-                return (char) Integer.parseInt(hex, 16);
+                try {
+                    return (char) Integer.parseInt(hex, 16);
+                } catch (NumberFormatException nfe) {
+                    throw syntaxError("Invalid escape sequence: " + hex);
+                }
 
             case 't':
                 return '\t';
diff --git a/json/src/test/java/org/json/JSONObjectTest.java b/json/src/test/java/org/json/JSONObjectTest.java
index e89db94..a1b7b13 100644
--- a/json/src/test/java/org/json/JSONObjectTest.java
+++ b/json/src/test/java/org/json/JSONObjectTest.java
@@ -1029,4 +1029,13 @@
         } catch (JSONException e) {
         }
     }
+
+    // https://code.google.com/p/android/issues/detail?id=103641
+    public void testInvalidUnicodeEscape() {
+        try {
+            new JSONObject("{\"q\":\"\\u\", \"r\":[]}");
+            fail();
+        } catch (JSONException expected) {
+        }
+    }
 }
diff --git a/json/src/test/java/org/json/JSONTokenerTest.java b/json/src/test/java/org/json/JSONTokenerTest.java
index de3a59a..1152e46 100644
--- a/json/src/test/java/org/json/JSONTokenerTest.java
+++ b/json/src/test/java/org/json/JSONTokenerTest.java
@@ -402,7 +402,6 @@
         try {
             new JSONTokener("abc\\u002\"").nextString('"');
             fail();
-        } catch (NumberFormatException e) {
         } catch (JSONException e) {
         }
         try {
@@ -413,13 +412,13 @@
         try {
             new JSONTokener("abc\\u    \"").nextString('"');
             fail();
-        } catch (NumberFormatException e) {
+        } catch (JSONException e) {
         }
         assertEquals("abc\"def", new JSONTokener("abc\\u0022def\"ghi").nextString('"'));
         try {
             new JSONTokener("abc\\u000G\"").nextString('"');
             fail();
-        } catch (NumberFormatException e) {
+        } catch (JSONException e) {
         }
     }
 
diff --git a/libart/src/main/java/dalvik/system/TransactionAbortError.java b/libart/src/main/java/dalvik/system/TransactionAbortError.java
new file mode 100644
index 0000000..cfe4ca2
--- /dev/null
+++ b/libart/src/main/java/dalvik/system/TransactionAbortError.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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;
+
+/**
+ * An exception only used by the compiler to abort a transaction.
+ *
+ * @hide
+ */
+final class TransactionAbortError extends InternalError {
+  /**
+   * Constructs a new {@code TransactionAbortError} with its stack trace filled in.
+   */
+  private TransactionAbortError() {
+  }
+
+  /**
+   * Constructs a new {@code TransactionAbortError} with its stack trace and detail
+   * message filled in.
+   *
+   * @param detailMessage the detail message for the exception.
+   */
+  private TransactionAbortError(String detailMessage) {
+      super(detailMessage);
+  }
+
+  /**
+   * Constructs a new {@code TransactionAbortError} with detail message and cause
+   * filled in.
+   *
+   * @param message the detail message for the exception.
+   * @param cause the detail cause for the exception.
+   */
+  private TransactionAbortError(String message, Throwable cause) {
+      super(message);
+      initCause(cause);
+  }
+
+  /**
+   * Constructs a new {@code TransactionAbortError} with its detail cause filled in.
+   *
+   * @param cause the detail cause for the exception.
+   */
+  private TransactionAbortError(Throwable cause) {
+      this(cause == null ? null : cause.toString(), cause);
+  }
+
+}
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 43fa00e..b885ed2 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -33,6 +33,10 @@
      */
     private static final VMRuntime THE_ONE = new VMRuntime();
 
+    // Note: Instruction set names are used to construct the names of some
+    // system properties. To be sure that the properties stay valid the
+    // instruction set name should not exceed 7 characters. See installd
+    // and the package manager for the actual propeties.
     private static final Map<String, String> ABI_TO_INSTRUCTION_SET_MAP
             = new HashMap<String, String>();
     static {
@@ -272,6 +276,12 @@
     public native void clearGrowthLimit();
 
     /**
+     * Make the current growth limit the new non growth limit capacity by releasing pages which
+     * are after the growth limit but before the non growth limit capacity.
+     */
+    public native void clampGrowthLimit();
+
+    /**
      * Returns true if either a Java debugger or native debugger is active.
      */
     public native boolean isDebuggerActive();
@@ -291,8 +301,13 @@
      */
     public native void registerNativeFree(int bytes);
 
-    public native void trimHeap();
+    public native void requestConcurrentGC();
     public native void concurrentGC();
+    public native void requestHeapTrim();
+    public native void trimHeap();
+    public native void startHeapTaskProcessor();
+    public native void stopHeapTaskProcessor();
+    public native void runHeapTasks();
 
     /**
      * Let the heap know of the new process state. This can change allocation and garbage collection
diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java
index ee0a0db..b69ab60 100644
--- a/libart/src/main/java/dalvik/system/VMStack.java
+++ b/libart/src/main/java/dalvik/system/VMStack.java
@@ -48,11 +48,10 @@
     native public static Class<?> getStackClass2();
 
     /**
-     * Returns the first ClassLoader on the call stack that isn't either of
-     * the passed-in ClassLoaders.
+     * Returns the first ClassLoader on the call stack that isn't the
+     * bootstrap class loader.
      */
-    public native static ClassLoader getClosestUserClassLoader(ClassLoader bootstrap,
-                                                               ClassLoader system);
+    public native static ClassLoader getClosestUserClassLoader();
 
     /**
      * Retrieves the stack trace from the specified thread.
diff --git a/libart/src/main/java/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java
index 8833776..99c562f 100644
--- a/libart/src/main/java/java/lang/Class.java
+++ b/libart/src/main/java/java/lang/Class.java
@@ -39,7 +39,6 @@
 import java.lang.annotation.Annotation;
 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;
@@ -55,6 +54,7 @@
 import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import libcore.reflect.AnnotationAccess;
 import libcore.reflect.GenericSignatureParser;
@@ -124,16 +124,16 @@
 
     private static final long serialVersionUID = 3206093459760846163L;
 
-    /** defining class loader, or NULL for the "bootstrap" system loader. */
+    /** 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.
+     * 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
+     * DexCache of resolved constant pool entries. Will be null for certain runtime-generated classes
      * e.g. arrays and primitive classes.
      */
     private transient DexCache dexCache;
@@ -145,16 +145,6 @@
     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
@@ -173,10 +163,7 @@
     /** 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. */
+    /** 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. */
@@ -197,6 +184,20 @@
     private transient int accessFlags;
 
     /**
+     * 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 long iFields;
+
+    /** Static fields */
+    private transient long sFields;
+
+
+    /**
      * Total size of the Class instance; used when allocating storage on GC heap.
      * See also {@link Class#objectSize}.
      */
@@ -221,32 +222,39 @@
      */
     private transient volatile int dexTypeIndex;
 
+    /** Number of instance fields. */
+    private transient int numInstanceFields;
+
     /** Number of instance fields that are object references. */
     private transient int numReferenceInstanceFields;
 
     /** Number of static fields that are object references. */
     private transient int numReferenceStaticFields;
 
+    /** Number of static fields. */
+    private transient int numStaticFields;
+
     /**
      * 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. */
+    /**
+     * The lower 16 bits is the 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
+        // Prevent this class from being instantiated,
+        // instances should be created by the runtime only.
     }
 
     /**
@@ -284,6 +292,9 @@
      * If the class has not yet been initialized and {@code shouldInitialize} is true,
      * the class will be initialized.
      *
+     * <p>If the provided {@code classLoader} is {@code null}, the bootstrap
+     * class loader will be used to load the class.
+     *
      * @throws ClassNotFoundException
      *             if the requested class cannot be found.
      * @throws LinkageError
@@ -296,7 +307,7 @@
             ClassLoader classLoader) throws ClassNotFoundException {
 
         if (classLoader == null) {
-            classLoader = ClassLoader.getSystemClassLoader();
+            classLoader = BootClassLoader.getInstance();
         }
         // Catch an Exception thrown by the underlying native code. It wraps
         // up everything inside a ClassNotFoundException, even if e.g. an
@@ -407,24 +418,7 @@
             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;
+        final ClassLoader loader = classLoader;
         return loader == null ? BootClassLoader.getInstance() : loader;
     }
 
@@ -456,10 +450,10 @@
      * @hide
      */
     public String getDexCacheString(Dex dex, int dexStringIndex) {
-        String s = dexCacheStrings[dexStringIndex];
+        String s = dexCache.getResolvedString(dexStringIndex);
         if (s == null) {
             s = dex.strings().get(dexStringIndex).intern();
-            dexCacheStrings[dexStringIndex] = s;
+            dexCache.setResolvedString(dexStringIndex, s);
         }
         return s;
     }
@@ -471,13 +465,12 @@
      * @hide
      */
     public Class<?> getDexCacheType(Dex dex, int dexTypeIndex) {
-        Class<?>[] dexCacheResolvedTypes = dexCache.resolvedTypes;
-        Class<?> resolvedType = dexCacheResolvedTypes[dexTypeIndex];
+        Class<?> resolvedType = dexCache.getResolvedType(dexTypeIndex);
         if (resolvedType == null) {
             int descriptorIndex = dex.typeIds().get(dexTypeIndex);
             String descriptor = getDexCacheString(dex, descriptorIndex);
             resolvedType = InternalNames.getClass(getClassLoader(), descriptor);
-            dexCacheResolvedTypes[dexTypeIndex] = resolvedType;
+            dexCache.setResolvedType(dexTypeIndex, resolvedType);
         }
         return resolvedType;
     }
@@ -534,30 +527,12 @@
     }
 
     /**
-     * Returns the constructor with the given parameters if it is defined by this class; null
-     * otherwise. This may return a non-public member.
+     * Returns the constructor with the given parameters if it is defined by this class;
+     * {@code 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;
-    }
+    private native Constructor<T> getDeclaredConstructorInternal(Class<?>[] args);
 
     /**
      * Returns an array containing {@code Constructor} objects for all public
@@ -568,9 +543,7 @@
      * @see #getDeclaredConstructors()
      */
     public Constructor<?>[] getConstructors() {
-        ArrayList<Constructor<T>> constructors = new ArrayList();
-        getDeclaredConstructors(true, constructors);
-        return constructors.toArray(new Constructor[constructors.size()]);
+        return getDeclaredConstructorsInternal(true);
     }
 
     /**
@@ -582,27 +555,10 @@
      * @see #getConstructors()
      */
     public Constructor<?>[] getDeclaredConstructors() {
-        ArrayList<Constructor<T>> constructors = new ArrayList();
-        getDeclaredConstructors(false, constructors);
-        return constructors.toArray(new Constructor[constructors.size()]);
+        return getDeclaredConstructorsInternal(false);
     }
 
-    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));
-                    }
-                }
-            }
-        }
-    }
+    private native Constructor<?>[] getDeclaredConstructorsInternal(boolean publicOnly);
 
     /**
      * Returns a {@code Method} object which represents the method matching the
@@ -689,68 +645,13 @@
     }
 
     /**
-     * Returns the method if it is defined by this class; null otherwise. This may return a
+     * Returns the method if it is defined by this class; {@code 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);
-    }
+    private native Method getDeclaredMethodInternal(String name, Class<?>[] args);
 
     /**
      * Returns an array containing {@code Method} objects for all methods
@@ -761,11 +662,7 @@
      * @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);
-        getDeclaredMethodsUnchecked(false, methods);
-        Method[] result = methods.toArray(new Method[methods.size()]);
+        Method[] result = getDeclaredMethodsUnchecked(false);
         for (Method m : result) {
             // Throw NoClassDefFoundError if types cannot be resolved.
             m.getReturnType();
@@ -783,30 +680,7 @@
      * @param methods A list to populate with declared methods.
      * @hide
      */
-    public void getDeclaredMethodsUnchecked(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));
-                    }
-                }
-            }
-        }
-    }
+    public native Method[] getDeclaredMethodsUnchecked(boolean publicOnly);
 
     /**
      * Returns an array containing {@code Method} objects for all public methods
@@ -836,11 +710,11 @@
      * superclasses, and all implemented interfaces, including overridden methods.
      */
     private void getPublicMethodsInternal(List<Method> result) {
-        getDeclaredMethodsUnchecked(true, result);
+        Collections.addAll(result, getDeclaredMethodsUnchecked(true));
         if (!isInterface()) {
             // Search superclasses, for interfaces don't search java.lang.Object.
             for (Class<?> c = superClass; c != null; c = c.superClass) {
-                c.getDeclaredMethodsUnchecked(true, result);
+                Collections.addAll(result, c.getDeclaredMethodsUnchecked(true));
             }
         }
         // Search iftable which has a flattened and uniqued list of interfaces.
@@ -848,7 +722,7 @@
         if (iftable != null) {
             for (int i = 0; i < iftable.length; i += 2) {
                 Class<?> ifc = (Class<?>) iftable[i];
-                ifc.getDeclaredMethodsUnchecked(true, result);
+                Collections.addAll(result, ifc.getDeclaredMethodsUnchecked(true));
             }
         }
     }
@@ -881,18 +755,7 @@
      * @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;
-    }
+    public native Field getDeclaredField(String name) throws NoSuchFieldException;
 
     /**
      * Returns an array containing {@code Field} objects for all fields declared
@@ -902,17 +765,7 @@
      *
      * @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);
-        getDeclaredFieldsUnchecked(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;
-    }
+    public native Field[] getDeclaredFields();
 
     /**
      * Populates a list of fields without performing any security or type
@@ -922,66 +775,18 @@
      * @param fields A list to populate with declared fields.
      * @hide
      */
-    public void getDeclaredFieldsUnchecked(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));
-                }
-            }
-        }
-    }
+    public native Field[] getDeclaredFieldsUnchecked(boolean publicOnly);
 
     /**
-     * Returns the field if it is defined by this class; null otherwise. This
+     * Returns the field if it is defined by this class; {@code null} otherwise. This
      * may return a non-public member.
      */
-    private Field getDeclaredFieldInternal(String name) {
-
-        if (iFields != null) {
-            final ArtField matched = findByName(name, iFields);
-            if (matched != null) {
-                return new Field(matched);
-            }
-        }
-        if (sFields != null) {
-            final ArtField matched = findByName(name, sFields);
-            if (matched != null) {
-                return new Field(matched);
-            }
-        }
-
-        return null;
-    }
+    private native Field getDeclaredFieldInternal(String name);
 
     /**
-     * Performs a binary search through {@code fields} for a field whose name
-     * is {@code name}. Returns {@code null} if no matching field exists.
+     * Returns the subset of getDeclaredFields which are public.
      */
-    private static ArtField findByName(String name, ArtField[] fields) {
-        int low = 0, high = fields.length - 1;
-        while (low <= high) {
-            final int mid = (low + high) >>> 1;
-            final ArtField f = fields[mid];
-            final int result = f.getName().compareTo(name);
-            if (result < 0) {
-                low = mid + 1;
-            } else if (result == 0) {
-                return f;
-            } else {
-                high = mid - 1;
-            }
-        }
-
-        return null;
-    }
+    private native Field[] getPublicDeclaredFields();
 
     /**
      * Returns the class that this class is a member of, or {@code null} if this
@@ -1080,8 +885,6 @@
         Field result = getPublicFieldRecursive(name);
         if (result == null) {
             throw new NoSuchFieldException(name);
-        } else {
-            result.getType();  // Throw NoClassDefFoundError if type cannot be resolved.
         }
         return result;
     }
@@ -1098,8 +901,7 @@
         // 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);
+                Field result = ((Class<?>) ifTable[i]).getPublicFieldRecursive(name);
                 if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
                     return result;
                 }
@@ -1123,11 +925,7 @@
     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;
+        return fields.toArray(new Field[fields.size()]);
     }
 
     /**
@@ -1137,15 +935,14 @@
     private void getPublicFieldsRecursive(List<Field> result) {
         // search superclasses
         for (Class<?> c = this; c != null; c = c.superClass) {
-            c.getDeclaredFieldsUnchecked(true, result);
+            Collections.addAll(result, c.getPublicDeclaredFields());
         }
 
         // 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.getDeclaredFieldsUnchecked(true, result);
+                Collections.addAll(result, ((Class<?>) iftable[i]).getPublicDeclaredFields());
             }
         }
     }
@@ -1295,21 +1092,21 @@
     }
 
     /**
-     * Returns the simple name of a member or local class, or null otherwise.
+     * Returns the simple name of a member or local class, or {@code null} otherwise.
      */
     private String getInnerClassName() {
         return AnnotationAccess.getInnerClassName(this);
     }
 
     /**
-     * Returns null.
+     * Returns {@code null}.
      */
     public ProtectionDomain getProtectionDomain() {
         return null;
     }
 
     /**
-     * Returns the URL of the given resource, or null if the resource is not found.
+     * Returns the URL of the given resource, or {@code 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
@@ -1340,8 +1137,8 @@
     }
 
     /**
-     * Returns a read-only stream for the contents of the given resource, or null if the resource
-     * is not found.
+     * Returns a read-only stream for the contents of the given resource, or {@code 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
@@ -1372,8 +1169,8 @@
     }
 
     /**
-     * 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
+     * Returns {@code 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}.)
      */
@@ -1547,7 +1344,7 @@
      * Tests whether this {@code Class} represents a primitive type.
      */
     public boolean isPrimitive() {
-      return primitiveType != 0;
+      return (primitiveType & 0xFFFF) != 0;
     }
 
     /**
@@ -1582,33 +1379,7 @@
      * @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(null, init.isAccessible());
-        } catch (InvocationTargetException e) {
-          SneakyThrow.sneakyThrow(e.getCause());
-          return null;  // Unreachable.
-        }
-    }
+    public native T newInstance() throws InstantiationException, IllegalAccessException;
 
     private boolean canAccess(Class<?> c) {
         if(Modifier.isPublic(c.accessFlags)) {
@@ -1664,7 +1435,7 @@
      * 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.
+        // TODO This might be a hack, but the runtime doesn't have the necessary info.
         ClassLoader loader = getClassLoader();
         if (loader != null) {
             String packageName = getPackageName$();
@@ -1674,7 +1445,7 @@
     }
 
     /**
-     * Returns the package name of this class. This returns null for classes in
+     * Returns the package name of this class. This returns {@code null} for classes in
      * the default package.
      *
      * @hide
diff --git a/libart/src/main/java/java/lang/ClassLoader.java b/libart/src/main/java/java/lang/ClassLoader.java
index 9079dc4..dfbeeb5 100644
--- a/libart/src/main/java/java/lang/ClassLoader.java
+++ b/libart/src/main/java/java/lang/ClassLoader.java
@@ -646,8 +646,8 @@
                 throw new IllegalArgumentException("Package " + name + " already defined");
             }
 
-            Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle,
-                    implVersion, implVendor, sealBase);
+            Package newPackage = new Package(this, name, specTitle, specVersion, specVendor,
+                    implTitle, implVersion, implVendor, sealBase);
 
             packages.put(name, newPackage);
 
diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java
index 485f2c9..43066e1 100644
--- a/libart/src/main/java/java/lang/Daemons.java
+++ b/libart/src/main/java/java/lang/Daemons.java
@@ -40,16 +40,14 @@
         ReferenceQueueDaemon.INSTANCE.start();
         FinalizerDaemon.INSTANCE.start();
         FinalizerWatchdogDaemon.INSTANCE.start();
-        HeapTrimmerDaemon.INSTANCE.start();
-        GCDaemon.INSTANCE.start();
+        HeapTaskDaemon.INSTANCE.start();
     }
 
     public static void stop() {
+        HeapTaskDaemon.INSTANCE.stop();
         ReferenceQueueDaemon.INSTANCE.stop();
         FinalizerDaemon.INSTANCE.stop();
         FinalizerWatchdogDaemon.INSTANCE.stop();
-        HeapTrimmerDaemon.INSTANCE.stop();
-        GCDaemon.INSTANCE.stop();
     }
 
     /**
@@ -59,12 +57,17 @@
      */
     private static abstract class Daemon implements Runnable {
         private Thread thread;
+        private String name;
+
+        protected Daemon(String name) {
+            this.name = name;
+        }
 
         public synchronized void start() {
             if (thread != null) {
                 throw new IllegalStateException("already running");
             }
-            thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName());
+            thread = new Thread(ThreadGroup.systemThreadGroup, this, name);
             thread.setDaemon(true);
             thread.start();
         }
@@ -80,6 +83,10 @@
         }
 
         public synchronized void interrupt() {
+            interrupt(thread);
+        }
+
+        public synchronized void interrupt(Thread thread) {
             if (thread == null) {
                 throw new IllegalStateException("not running");
             }
@@ -99,7 +106,7 @@
             if (threadToStop == null) {
                 throw new IllegalStateException("not running");
             }
-            threadToStop.interrupt();
+            interrupt(threadToStop);
             while (true) {
                 try {
                     threadToStop.join();
@@ -125,6 +132,10 @@
     private static class ReferenceQueueDaemon extends Daemon {
         private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
 
+        ReferenceQueueDaemon() {
+            super("ReferenceQueueDaemon");
+        }
+
         @Override public void run() {
             while (isRunning()) {
                 Reference<?> list;
@@ -144,20 +155,14 @@
         }
 
         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();
-            }
+            Reference<?> start = list;
+            do {
+                // pendingNext is owned by the GC so no synchronization is required.
+                Reference<?> next = list.pendingNext;
+                list.pendingNext = null;
+                list.enqueueInternal();
+                list = next;
+            } while (list != start);
         }
     }
 
@@ -167,6 +172,10 @@
         private volatile Object finalizingObject;
         private volatile long finalizingStartedNanos;
 
+        FinalizerDaemon() {
+            super("FinalizerDaemon");
+        }
+
         @Override public void run() {
             while (isRunning()) {
                 // Take a reference, blocking until one is ready or the thread should stop
@@ -207,6 +216,10 @@
     private static class FinalizerWatchdogDaemon extends Daemon {
         private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon();
 
+        FinalizerWatchdogDaemon() {
+            super("FinalizerWatchdogDaemon");
+        }
+
         @Override public void run() {
             while (isRunning()) {
                 boolean waitSuccessful = waitForObject();
@@ -294,59 +307,42 @@
         }
     }
 
-    // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap.
+    // Adds a heap trim task ot the heap event processor, not called from java. Left for
+    // compatibility purposes due to reflection.
     public static void requestHeapTrim() {
-        synchronized (HeapTrimmerDaemon.INSTANCE) {
-            HeapTrimmerDaemon.INSTANCE.notify();
-        }
+        VMRuntime.getRuntime().requestHeapTrim();
     }
 
-    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.
+    // Adds a concurrent GC request task ot the heap event processor, not called from java. Left
+    // for compatibility purposes due to reflection.
     public static void requestGC() {
-        GCDaemon.INSTANCE.requestGC();
+        VMRuntime.getRuntime().requestConcurrentGC();
     }
 
-    private static class GCDaemon extends Daemon {
-        private static final GCDaemon INSTANCE = new GCDaemon();
-        private static final AtomicBoolean atomicBoolean = new AtomicBoolean();
+    private static class HeapTaskDaemon extends Daemon {
+        private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon();
 
-        public void requestGC() {
-            if (atomicBoolean.getAndSet(true)) {
-              return;
-            }
-            synchronized (this) {
-                notify();
-            }
-            atomicBoolean.set(false);
+        HeapTaskDaemon() {
+            super("HeapTaskDaemon");
+        }
+
+        // Overrides the Daemon.interupt method which is called from Daemons.stop.
+        public synchronized void interrupt(Thread thread) {
+            VMRuntime.getRuntime().stopHeapTaskProcessor();
         }
 
         @Override public void run() {
-            while (isRunning()) {
-                try {
-                    synchronized (this) {
-                        // Wait until a request comes in.
-                        wait();
-                    }
-                    VMRuntime.getRuntime().concurrentGC();
-                } catch (InterruptedException ignored) {
+            synchronized (this) {
+                if (isRunning()) {
+                  // Needs to be synchronized or else we there is a race condition where we start
+                  // the thread, call stopHeapTaskProcessor before we start the heap task
+                  // processor, resulting in a deadlock since startHeapTaskProcessor restarts it
+                  // while the other thread is waiting in Daemons.stop().
+                  VMRuntime.getRuntime().startHeapTaskProcessor();
                 }
             }
+            // This runs tasks until we are stopped and there is no more pending task.
+            VMRuntime.getRuntime().runHeapTasks();
         }
     }
 }
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
index e4caffa..c047018 100644
--- a/libart/src/main/java/java/lang/DexCache.java
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -33,7 +33,6 @@
 package java.lang;
 
 import com.android.dex.Dex;
-import java.lang.reflect.ArtField;
 import java.lang.reflect.ArtMethod;
 
 /**
@@ -47,18 +46,18 @@
     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 fields as they become resolved following interpreter semantics. May refer to
+     * fields defined in other dex files. Either an int array or long array.
+     */
+    private Object resolvedFields;
+
+    /**
      * References to types as they become resolved following interpreter semantics. May refer to
      * types defined in other dex files.
      */
@@ -89,6 +88,10 @@
         return result;
     }
 
+    native Class<?> getResolvedType(int typeIndex);
+    native String getResolvedString(int stringIndex);
+    native void setResolvedType(int typeIndex, Class<?> type);
+    native void setResolvedString(int stringIndex, String string);
     private native Dex getDexNative();
 }
 
diff --git a/libart/src/main/java/java/lang/Object.java b/libart/src/main/java/java/lang/Object.java
index 20fdbf9..5b820e3 100644
--- a/libart/src/main/java/java/lang/Object.java
+++ b/libart/src/main/java/java/lang/Object.java
@@ -274,10 +274,11 @@
      */
     public int hashCode() {
         int lockWord = shadow$_monitor_;
-        final int lockWordMask = 0xC0000000;  // Top 2 bits.
+        final int lockWordStateMask = 0xC0000000;  // Top 2 bits.
         final int lockWordStateHash = 0x80000000;  // Top 2 bits are value 2 (kStateHash).
-        if ((lockWord & lockWordMask) == lockWordStateHash) {
-            return lockWord & ~lockWordMask;
+        final int lockWordHashMask = 0x0FFFFFFF;  // Low 28 bits.
+        if ((lockWord & lockWordStateMask) == lockWordStateHash) {
+            return lockWord & lockWordHashMask;
         }
         return System.identityHashCode(this);
     }
diff --git a/libart/src/main/java/java/lang/String.java b/libart/src/main/java/java/lang/String.java
index 0107b6e..a5bf34c 100644
--- a/libart/src/main/java/java/lang/String.java
+++ b/libart/src/main/java/java/lang/String.java
@@ -22,12 +22,12 @@
 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.CharsetUtils;
 import libcore.util.EmptyArray;
 
 /**
@@ -337,12 +337,12 @@
             this.offset = 0;
             this.value = new char[byteCount];
             this.count = byteCount;
-            Charsets.isoLatin1BytesToChars(data, offset, byteCount, value);
+            CharsetUtils.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);
+            CharsetUtils.asciiBytesToChars(data, offset, byteCount, value);
         } else {
             CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount));
             this.offset = 0;
@@ -772,13 +772,13 @@
     public byte[] getBytes(Charset charset) {
         String canonicalCharsetName = charset.name();
         if (canonicalCharsetName.equals("UTF-8")) {
-            return Charsets.toUtf8Bytes(value, offset, count);
+            return CharsetUtils.toUtf8Bytes(value, offset, count);
         } else if (canonicalCharsetName.equals("ISO-8859-1")) {
-            return Charsets.toIsoLatin1Bytes(value, offset, count);
+            return CharsetUtils.toIsoLatin1Bytes(value, offset, count);
         } else if (canonicalCharsetName.equals("US-ASCII")) {
-            return Charsets.toAsciiBytes(value, offset, count);
+            return CharsetUtils.toAsciiBytes(value, offset, count);
         } else if (canonicalCharsetName.equals("UTF-16BE")) {
-            return Charsets.toBigEndianUtf16Bytes(value, offset, count);
+            return CharsetUtils.toBigEndianUtf16Bytes(value, offset, count);
         } else {
             CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count);
             ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer());
diff --git a/libart/src/main/java/java/lang/Thread.java b/libart/src/main/java/java/lang/Thread.java
index 852e2cf..7b3666b 100644
--- a/libart/src/main/java/java/lang/Thread.java
+++ b/libart/src/main/java/java/lang/Thread.java
@@ -1158,7 +1158,7 @@
      *
      * @hide for Unsafe
      */
-    public void unpark() {
+    public final void unpark$() {
         synchronized (lock) {
             switch (parkState) {
                 case ParkState.PREEMPTIVELY_UNPARKED: {
@@ -1204,7 +1204,7 @@
      *
      * @hide for Unsafe
      */
-    public void parkFor(long nanos) {
+    public final void parkFor$(long nanos) {
         synchronized (lock) {
             switch (parkState) {
                 case ParkState.PREEMPTIVELY_UNPARKED: {
@@ -1260,7 +1260,7 @@
      *
      * @hide for Unsafe
      */
-    public void parkUntil(long time) {
+    public final void parkUntil$(long time) {
         synchronized (lock) {
             /*
              * Note: This conflates the two time bases of "wall clock"
@@ -1281,7 +1281,7 @@
             if (delayMillis <= 0) {
                 parkState = ParkState.UNPARKED;
             } else {
-                parkFor(delayMillis * NANOS_PER_MILLI);
+                parkFor$(delayMillis * NANOS_PER_MILLI);
             }
         }
     }
diff --git a/libart/src/main/java/java/lang/reflect/AbstractMethod.java b/libart/src/main/java/java/lang/reflect/AbstractMethod.java
index 7e6491d..0ac15f9 100644
--- a/libart/src/main/java/java/lang/reflect/AbstractMethod.java
+++ b/libart/src/main/java/java/lang/reflect/AbstractMethod.java
@@ -39,28 +39,38 @@
 import libcore.reflect.GenericSignatureParser;
 import libcore.reflect.ListOfTypes;
 import libcore.reflect.Types;
+import libcore.util.EmptyArray;
 
 /**
  * This class represents an abstract method. Abstract methods are either methods or constructors.
  * @hide
  */
 public abstract class AbstractMethod extends AccessibleObject {
+    /** Bits encoding access (e.g. public, private) as well as other runtime specific flags */
+    protected int accessFlags;
+
+    /**
+     * The ArtMethod associated with this Method, requried for dispatching due to entrypoints
+     * Hidden to workaround b/16828157.
+     * @hide
+     */
+    protected ArtMethod artMethod;
+
+    /** Method's declaring class */
+    protected Class<?> declaringClass;
+
+    /** Overriden method's declaring class (same as declaringClass unless declaringClass
+     * is a proxy class) */
+    protected Class<?> declaringClassOfOverriddenMethod;
+
+    /** The method index of this method within its defining dex file */
+    protected int dexMethodIndex;
 
     /**
      * Hidden to workaround b/16828157.
      * @hide
      */
-    protected final ArtMethod artMethod;
-
-    /**
-     * Hidden to workaround b/16828157.
-     * @hide
-     */
-    protected AbstractMethod(ArtMethod artMethod) {
-        if (artMethod == null) {
-            throw new NullPointerException("artMethod == null");
-        }
-        this.artMethod = artMethod;
+    protected AbstractMethod() {
     }
 
     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
@@ -90,33 +100,33 @@
     }
 
     int getModifiers() {
-        return fixMethodFlags(artMethod.getAccessFlags());
+        return fixMethodFlags(accessFlags);
     }
 
     boolean isVarArgs() {
-        return (artMethod.getAccessFlags() & Modifier.VARARGS) != 0;
+        return (accessFlags & Modifier.VARARGS) != 0;
     }
 
     boolean isBridge() {
-        return (artMethod.getAccessFlags() & Modifier.BRIDGE) != 0;
+        return (accessFlags & Modifier.BRIDGE) != 0;
     }
 
     boolean isSynthetic() {
-        return (artMethod.getAccessFlags() & Modifier.SYNTHETIC) != 0;
+        return (accessFlags & Modifier.SYNTHETIC) != 0;
     }
 
     /**
      * @hide
      */
     public final int getAccessFlags() {
-        return artMethod.getAccessFlags();
+        return accessFlags;
     }
 
     /**
      * Returns the class that declares this constructor or method.
      */
     Class<?> getDeclaringClass() {
-        return artMethod.getDeclaringClass();
+        return declaringClass;
     }
 
     /**
@@ -125,7 +135,7 @@
      * @hide
      */
     public final int getDexMethodIndex() {
-        return artMethod.getDexMethodIndex();
+        return dexMethodIndex;
     }
 
     /**
@@ -144,7 +154,17 @@
      * @return the parameter types
      */
     Class<?>[] getParameterTypes() {
-        return artMethod.getParameterTypes();
+        Dex dex = declaringClassOfOverriddenMethod.getDex();
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex);
+        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] = declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]);
+        }
+        return parametersArray;
     }
 
     /**
@@ -155,8 +175,10 @@
         if (!(other instanceof AbstractMethod)) {
             return false;
         }
-        // exactly one instance of each member in this runtime
-        return this.artMethod == ((AbstractMethod) other).artMethod;
+        // Exactly one instance of each member in this runtime, todo, does this work for proxies?
+        AbstractMethod otherMethod = (AbstractMethod) other;
+        return this.declaringClass == otherMethod.declaringClass &&
+            this.dexMethodIndex == otherMethod.dexMethodIndex;
     }
 
     String toGenericString() {
@@ -252,6 +274,37 @@
                                parser.returnType, parser.formalTypeParameters);
     }
 
+    protected boolean equalMethodParameters(Class<?>[] params) {
+        Dex dex = declaringClassOfOverriddenMethod.getDex();
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex);
+        if (types.length != params.length) {
+            return false;
+        }
+        for (int i = 0; i < types.length; i++) {
+            if (declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]) != params[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    protected int compareParameters(Class<?>[] params) {
+        Dex dex = declaringClassOfOverriddenMethod.getDex();
+        short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex);
+        int length = Math.min(types.length, params.length);
+        for (int i = 0; i < length; i++) {
+            Class<?> aType = declaringClassOfOverriddenMethod.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;
+    }
+
     /**
      * Helper for Method and Constructor for toGenericString
      */
diff --git a/libart/src/main/java/java/lang/reflect/AccessibleObject.java b/libart/src/main/java/java/lang/reflect/AccessibleObject.java
index a1e2743..f623880 100644
--- a/libart/src/main/java/java/lang/reflect/AccessibleObject.java
+++ b/libart/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -71,15 +71,8 @@
      * IllegalAccessExceptions}.
      */
     public void setAccessible(boolean flag) {
-        try {
-          if (equals(Class.class.getDeclaredConstructor())) {
-            throw new SecurityException("Can't make class constructor accessible");
-          }
-        } catch (NoSuchMethodException e) {
-          throw new AssertionError("Couldn't find class constructor");
-        }
         this.flag = flag;
-     }
+    }
 
     /**
      * Attempts to set the accessible flag for all objects in {@code objects}.
diff --git a/libart/src/main/java/java/lang/reflect/ArtField.java b/libart/src/main/java/java/lang/reflect/ArtField.java
deleted file mode 100644
index 6fdcdb2..0000000
--- a/libart/src/main/java/java/lang/reflect/ArtField.java
+++ /dev/null
@@ -1,96 +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.
- */
-/*
- * 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
index 95e03c6..84e6b48 100644
--- a/libart/src/main/java/java/lang/reflect/ArtMethod.java
+++ b/libart/src/main/java/java/lang/reflect/ArtMethod.java
@@ -75,138 +75,4 @@
 
     /** Only created by ART directly. */
     private ArtMethod() {}
-
-    Class getDeclaringClass() {
-        return declaringClass;
-    }
-
-    public int getAccessFlags() {
-        return accessFlags;
-    }
-
-    int getDexMethodIndex() {
-        return dexMethodIndex;
-    }
-
-    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(dexMethodIndex);
-        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(dexMethodIndex);
-        // 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(dexMethodIndex);
-        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, dexMethodIndex);
-    }
-
-    /**
-     * 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) {
-        return declaringClass.getDexCacheString(dex, dexStringIndex);
-    }
-
-    /**
-     * 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[dexMethodIndex];
-        }
-        return this;
-    }
 }
diff --git a/libart/src/main/java/java/lang/reflect/Constructor.java b/libart/src/main/java/java/lang/reflect/Constructor.java
index 2eb12b0..9711ef4 100644
--- a/libart/src/main/java/java/lang/reflect/Constructor.java
+++ b/libart/src/main/java/java/lang/reflect/Constructor.java
@@ -49,11 +49,7 @@
 
     private static final Comparator<Method> ORDER_BY_SIGNATURE = null; // Unused; must match Method.
 
-    /**
-     * @hide
-     */
-    public Constructor(ArtMethod artMethod) {
-        super(artMethod);
+    private Constructor() {
     }
 
     public Annotation[] getAnnotations() {
@@ -213,7 +209,8 @@
      * @return an array of arrays of {@code Annotation} instances
      */
     public Annotation[][] getParameterAnnotations() {
-        return artMethod.getParameterAnnotations();
+        return AnnotationAccess.getParameterAnnotations(
+            declaringClassOfOverriddenMethod, dexMethodIndex);
     }
 
     /**
@@ -283,13 +280,7 @@
      *
      * @see AccessibleObject
      */
-    public T newInstance(Object... args) throws InstantiationException,
-            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
-      return newInstance(args, isAccessible());
-    }
-
-    /** @hide */
-    public native T newInstance(Object[] args, boolean accessible) throws InstantiationException,
+    public native T newInstance(Object... args) throws InstantiationException,
             IllegalAccessException, IllegalArgumentException, InvocationTargetException;
 
     /**
@@ -330,4 +321,20 @@
 
         return result.toString();
     }
+
+    /**
+     * Attempts to set the accessible flag. Setting this to true prevents {@code
+     * IllegalAccessExceptions}.
+     */
+    public void setAccessible(boolean flag) {
+        Class<?> declaringClass = getDeclaringClass();
+        if (declaringClass == Class.class) {
+            throw new SecurityException("Can't make class constructor accessible");
+        } else if (declaringClass == Field.class) {
+            throw new SecurityException("Can't make field constructor accessible");
+        } else if (declaringClass == Method.class) {
+            throw new SecurityException("Can't make method constructor accessible");
+        }
+        super.setAccessible(flag);
+    }
 }
diff --git a/libart/src/main/java/java/lang/reflect/Field.java b/libart/src/main/java/java/lang/reflect/Field.java
index 11e8a6e..37f2ad0 100644
--- a/libart/src/main/java/java/lang/reflect/Field.java
+++ b/libart/src/main/java/java/lang/reflect/Field.java
@@ -71,16 +71,13 @@
         }
     };
 
-    private final ArtField artField;
+    private int accessFlags;
+    private Class<?> declaringClass;
+    private int dexFieldIndex;
+    private int offset;
+    private Class<?> type;
 
-    /**
-     * @hide
-     */
-    public Field(ArtField artField) {
-        if (artField == null) {
-            throw new NullPointerException("artField == null");
-        }
-        this.artField = artField;
+    private Field() {
     }
 
     /**
@@ -91,7 +88,7 @@
      * @see Modifier
      */
     @Override public int getModifiers() {
-        return artField.getAccessFlags() & 0xffff;  // mask out bits not used by Java
+        return accessFlags & 0xffff;  // mask out bits not used by Java
     }
 
     /**
@@ -101,7 +98,7 @@
      *         false} otherwise
      */
     public boolean isEnumConstant() {
-        return (artField.getAccessFlags() & Modifier.ENUM) != 0;
+        return (accessFlags & Modifier.ENUM) != 0;
     }
 
     /**
@@ -110,7 +107,7 @@
      * @return {@code true} if this field is synthetic, {@code false} otherwise
      */
     @Override public boolean isSynthetic() {
-        return (artField.getAccessFlags() & Modifier.SYNTHETIC) != 0;
+        return (accessFlags & Modifier.SYNTHETIC) != 0;
     }
 
     /**
@@ -119,11 +116,20 @@
      * @return the name of this field
      */
     @Override public String getName() {
-        return artField.getName();
+        if (dexFieldIndex == -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(dexFieldIndex);
+        return declaringClass.getDexCacheString(dex, nameIndex);
     }
 
     @Override public Class<?> getDeclaringClass() {
-        return artField.getDeclaringClass();
+        return declaringClass;
     }
 
     /**
@@ -132,7 +138,7 @@
      * @return the type of this field
      */
     public Class<?> getType() {
-        return artField.getType();
+        return type;
     }
 
     /**
@@ -141,7 +147,7 @@
      * @hide
      */
     public int getDexFieldIndex() {
-        return artField.getDexFieldIndex();
+        return dexFieldIndex;
     }
 
     /**
@@ -150,7 +156,7 @@
      * @hide
      */
     public int getOffset() {
-        return artField.getOffset();
+        return offset;
     }
 
     /**
@@ -170,8 +176,10 @@
         if (!(other instanceof Field)) {
             return false;
         }
-        // exactly one instance of each member in this runtime
-        return this.artField == ((Field) other).artField;
+        // Given same declaring class and offset, it must be the same field since no two distinct
+        // fields can have the same offset.
+        Field field = (Field)other;
+        return this.declaringClass == field.declaringClass && this.offset == field.offset;
     }
 
     /**
@@ -182,10 +190,12 @@
      */
     public String toGenericString() {
         StringBuilder sb = new StringBuilder(80);
+        // Limit modifier bits to the ones that toStringGeneric should return for fields.
+
+        String modifiers = Modifier.getDeclarationFieldModifiers(getModifiers());
         // append modifiers if any
-        int modifier = getModifiers();
-        if (modifier != 0) {
-            sb.append(Modifier.toString(modifier)).append(' ');
+        if (!modifiers.isEmpty()) {
+            sb.append(modifiers).append(' ');
         }
         // append generic type
         Types.appendGenericType(sb, getGenericType());
@@ -275,11 +285,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public Object get(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return get(object, isAccessible());
-    }
-
-    private native Object get(Object object, boolean accessible)
+    public native Object get(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -305,12 +311,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public boolean getBoolean(Object object) throws IllegalAccessException,
-                                                    IllegalArgumentException {
-        return getBoolean(object, isAccessible());
-    }
-
-    private native boolean getBoolean(Object object, boolean accessible)
+    public native boolean getBoolean(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -336,11 +337,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public byte getByte(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return getByte(object, isAccessible());
-    }
-
-    private native byte getByte(Object object, boolean accessible)
+    public native byte getByte(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -366,11 +363,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public char getChar(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return getChar(object, isAccessible());
-    }
-
-    private native char getChar(Object object, boolean accessible)
+    public native char getChar(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -396,11 +389,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public double getDouble(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return getDouble(object, isAccessible());
-    }
-
-    private native double getDouble(Object object, boolean accessible)
+    public native double getDouble(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -426,11 +415,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public float getFloat(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return getFloat(object, isAccessible());
-    }
-
-    private native float getFloat(Object object, boolean accessible)
+    public native float getFloat(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -456,11 +441,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public int getInt(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return getInt(object, isAccessible());
-    }
-
-    private native int getInt(Object object, boolean accessible)
+    public native int getInt(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -486,11 +467,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public long getLong(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return getLong(object, isAccessible());
-    }
-
-    private native long getLong(Object object, boolean accessible)
+    public native long getLong(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -516,11 +493,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public short getShort(Object object) throws IllegalAccessException, IllegalArgumentException {
-        return getShort(object, isAccessible());
-    }
-
-    private native short getShort(Object object, boolean accessible)
+    public native short getShort(Object object)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -552,12 +525,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void set(Object object, Object value) throws IllegalAccessException,
-                                                        IllegalArgumentException {
-        set(object, value, isAccessible());
-    }
-
-    private native void set(Object object, Object value, boolean accessible)
+    public native void set(Object object, Object value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -588,12 +556,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setBoolean(Object object, boolean value) throws IllegalAccessException,
-                                                                IllegalArgumentException {
-        setBoolean(object, value, isAccessible());
-    }
-
-    private native void setBoolean(Object object, boolean value, boolean accessible)
+    public native void setBoolean(Object object, boolean value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -623,14 +586,8 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setByte(Object object, byte value) throws IllegalAccessException,
-                                                          IllegalArgumentException {
-        setByte(object, value, isAccessible());
-    }
-
-    private native void setByte(Object object, byte value, boolean 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}
@@ -658,12 +615,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setChar(Object object, char value) throws IllegalAccessException,
-                                                          IllegalArgumentException {
-        setChar(object, value, isAccessible());
-    }
-
-    private native void setChar(Object object, char value, boolean accessible)
+    public native void setChar(Object object, char value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -693,12 +645,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setDouble(Object object, double value) throws IllegalAccessException,
-                                                              IllegalArgumentException {
-        setDouble(object, value, isAccessible());
-    }
-
-    private native void setDouble(Object object, double value, boolean accessible)
+    public native void setDouble(Object object, double value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -728,12 +675,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setFloat(Object object, float value) throws IllegalAccessException,
-                                                            IllegalArgumentException {
-        setFloat(object, value, isAccessible());
-    }
-
-    private native void setFloat(Object object, float value, boolean accessible)
+    public native void setFloat(Object object, float value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -763,12 +705,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setInt(Object object, int value) throws IllegalAccessException,
-                                                        IllegalArgumentException {
-        setInt(object, value, isAccessible());
-    }
-
-    private native void setInt(Object object, int value, boolean accessible)
+    public native void setInt(Object object, int value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -798,12 +735,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setLong(Object object, long value) throws IllegalAccessException,
-                                                          IllegalArgumentException {
-        setLong(object, value, isAccessible());
-    }
-
-    private native void setLong(Object object, long value, boolean accessible)
+    public native void setLong(Object object, long value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -833,12 +765,7 @@
      * @throws IllegalAccessException
      *             if this field is not accessible
      */
-    public void setShort(Object object, short value) throws IllegalAccessException,
-                                                            IllegalArgumentException {
-        setShort(object, value, isAccessible());
-    }
-
-    private native void setShort(Object object, short value, boolean accessible)
+    public native void setShort(Object object, short value)
             throws IllegalAccessException, IllegalArgumentException;
 
     /**
@@ -861,7 +788,8 @@
      */
     @Override
     public String toString() {
-        StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+        StringBuilder result = new StringBuilder(
+                Modifier.getDeclarationFieldModifiers(getModifiers()));
         if (result.length() != 0) {
             result.append(' ');
         }
diff --git a/libart/src/main/java/java/lang/reflect/Method.java b/libart/src/main/java/java/lang/reflect/Method.java
index 058fb96..a07ec6f 100644
--- a/libart/src/main/java/java/lang/reflect/Method.java
+++ b/libart/src/main/java/java/lang/reflect/Method.java
@@ -57,8 +57,7 @@
             }
             int comparison = a.getName().compareTo(b.getName());
             if (comparison == 0) {
-                comparison = a.artMethod.findOverriddenMethodIfProxy().compareParameters(
-                        b.getParameterTypes());
+                comparison = a.compareParameters(b.getParameterTypes());
                 if (comparison == 0) {
                     // This is necessary for methods that have covariant return types.
                     Class<?> aReturnType = a.getReturnType();
@@ -77,12 +76,7 @@
     /**
      * @hide
      */
-    public Method(ArtMethod artMethod) {
-        super(artMethod);
-    }
-
-    ArtMethod getArtMethod() {
-        return artMethod;
+    private Method() {
     }
 
     public Annotation[] getAnnotations() {
@@ -136,7 +130,9 @@
      * @return the name of this method
      */
     @Override public String getName() {
-        return ArtMethod.getMethodName(artMethod);
+        Dex dex = declaringClassOfOverriddenMethod.getDex();
+        int nameIndex = dex.nameIndexFromMethodIndex(dexMethodIndex);
+        return declaringClassOfOverriddenMethod.getDexCacheString(dex, nameIndex);
     }
 
     /**
@@ -171,7 +167,7 @@
      * @return the parameter types
      */
     @Override public Class<?>[] getParameterTypes() {
-        return artMethod.findOverriddenMethodIfProxy().getParameterTypes();
+        return super.getParameterTypes();
     }
 
     /**
@@ -181,9 +177,13 @@
      * @return the return type
      */
     public Class<?> getReturnType() {
-        return artMethod.findOverriddenMethodIfProxy().getReturnType();
+        Dex dex = declaringClassOfOverriddenMethod.getDex();
+        int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(dexMethodIndex);
+        // Note, in the case of a Proxy the dex cache types are equal.
+        return declaringClassOfOverriddenMethod.getDexCacheType(dex, returnTypeIndex);
     }
 
+
     /**
      * {@inheritDoc}
      *
@@ -209,8 +209,7 @@
      * @hide needed by Proxy
      */
     boolean equalNameAndParameters(Method m) {
-        return getName().equals(m.getName()) &&
-                ArtMethod.equalMethodParameters(artMethod,m.getParameterTypes());
+        return getName().equals(m.getName()) && equalMethodParameters(m.getParameterTypes());
     }
 
     /**
@@ -310,7 +309,8 @@
      * @return an array of arrays of {@code Annotation} instances
      */
     public Annotation[][] getParameterAnnotations() {
-        return artMethod.findOverriddenMethodIfProxy().getParameterAnnotations();
+        return AnnotationAccess.getParameterAnnotations(
+            declaringClassOfOverriddenMethod, dexMethodIndex);
     }
 
     /**
@@ -367,12 +367,7 @@
      * @throws InvocationTargetException
      *             if an exception was thrown by the invoked method
      */
-    public Object invoke(Object receiver, Object... args)
-            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
-        return invoke(receiver, args, isAccessible());
-    }
-
-    private native Object invoke(Object receiver, Object[] args, boolean accessible)
+    public native Object invoke(Object receiver, Object... args)
             throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
 
     /**
@@ -398,7 +393,8 @@
      */
     @Override
     public String toString() {
-        StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+        StringBuilder result = new StringBuilder(
+                Modifier.getDeclarationMethodModifiers(getModifiers()));
 
         if (result.length() != 0) {
             result.append(' ');
diff --git a/libart/src/main/java/java/lang/reflect/Proxy.java b/libart/src/main/java/java/lang/reflect/Proxy.java
old mode 100644
new mode 100755
index 31f9cd9..18ad49c
--- a/libart/src/main/java/java/lang/reflect/Proxy.java
+++ b/libart/src/main/java/java/lang/reflect/Proxy.java
@@ -166,11 +166,7 @@
         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();
-        }
+        Method[] methodsArray = methods.toArray(new Method[methods.size()]);
         Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
 
         String baseName = commonPackageName != null && !commonPackageName.isEmpty()
@@ -179,7 +175,7 @@
 
         Class<?> result;
         synchronized (loader.proxyCache) {
-            result = loader.proxyCache.get(interfaceSet);
+            result = loader.proxyCache.get(interfaceList);
             if (result == null) {
                 String name = baseName + nextClassNameIndex++;
                 result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
@@ -383,7 +379,7 @@
     }
 
     private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
-                                                 ClassLoader loader, ArtMethod[] methods,
+                                                 ClassLoader loader, Method[] methods,
                                                  Class<?>[][] exceptions);
 
     /*
@@ -392,8 +388,8 @@
      */
     private static native void constructorPrototype(InvocationHandler h);
 
-    static Object invoke(Proxy proxy, ArtMethod method, Object[] args) throws Throwable {
+    private static Object invoke(Proxy proxy, Method method, Object[] args) throws Throwable {
         InvocationHandler h = proxy.h;
-        return h.invoke(proxy, new Method(method), args);
+        return h.invoke(proxy, method, args);
     }
 }
diff --git a/libart/src/main/java/sun/misc/Unsafe.java b/libart/src/main/java/sun/misc/Unsafe.java
index 6f5f5ee..1f938de 100644
--- a/libart/src/main/java/sun/misc/Unsafe.java
+++ b/libart/src/main/java/sun/misc/Unsafe.java
@@ -294,9 +294,9 @@
      */
     public void park(boolean absolute, long time) {
         if (absolute) {
-            Thread.currentThread().parkUntil(time);
+            Thread.currentThread().parkUntil$(time);
         } else {
-            Thread.currentThread().parkFor(time);
+            Thread.currentThread().parkFor$(time);
         }
     }
 
@@ -310,7 +310,7 @@
      */
     public void unpark(Object obj) {
         if (obj instanceof Thread) {
-            ((Thread) obj).unpark();
+            ((Thread) obj).unpark$();
         } else {
             throw new IllegalArgumentException("valid for Threads only");
         }
diff --git a/luni/src/benchmark/native/libcore_io_Memory_bench.cpp b/luni/src/benchmark/native/libcore_io_Memory_bench.cpp
new file mode 100644
index 0000000..0819c27
--- /dev/null
+++ b/luni/src/benchmark/native/libcore_io_Memory_bench.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// The functions we want to benchmark are static, so include the source code.
+#include "luni/src/main/native/libcore_io_Memory.cpp"
+
+#include <benchmark/Benchmark.h>
+
+template<typename T, size_t ALIGN>
+void swap_bench(testing::Benchmark* bench, void (*swap_func)(T*, const T*, size_t),
+                int iters, size_t num_elements) {
+  T* src;
+  T* dst;
+  T* src_elems;
+  T* dst_elems;
+
+  if (ALIGN) {
+    src_elems = new T[num_elements + 1];
+    dst_elems = new T[num_elements + 1];
+
+    src = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(src_elems) + ALIGN);
+    dst = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(dst_elems) + ALIGN);
+  } else {
+    src_elems = new T[num_elements];
+    dst_elems = new T[num_elements];
+
+    src = src_elems;
+    dst = dst_elems;
+  }
+
+  memset(dst, 0, sizeof(T) * num_elements);
+  memset(src, 0x12, sizeof(T) * num_elements);
+
+  bench->StartBenchmarkTiming();
+
+  for (int i = 0; i < iters; i++) {
+    swap_func(src, dst, num_elements);
+  }
+
+  bench->StopBenchmarkTiming();
+
+  delete[] src_elems;
+  delete[] dst_elems;
+}
+
+#define AT_COMMON_VALUES \
+    Arg(10)->Arg(100)->Arg(1000)->Arg(1024*10)->Arg(1024*100)
+
+BENCHMARK_WITH_ARG(BM_libcore_swapShorts_aligned, int)->AT_COMMON_VALUES;
+void BM_libcore_swapShorts_aligned::Run(int iters, int num_shorts) {
+  swap_bench<jshort, 0>(this, swapShorts, iters, num_shorts);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapInts_aligned, int)->AT_COMMON_VALUES;
+void BM_libcore_swapInts_aligned::Run(int iters, int num_ints) {
+  swap_bench<jint, 0>(this, swapInts, iters, num_ints);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapLongs_aligned, int)->AT_COMMON_VALUES;
+void BM_libcore_swapLongs_aligned::Run(int iters, int num_longs) {
+  swap_bench<jlong, 0>(this, swapLongs, iters, num_longs);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapShorts_unaligned1, int)->AT_COMMON_VALUES;
+void BM_libcore_swapShorts_unaligned1::Run(int iters, int num_shorts) {
+  swap_bench<jshort, 1>(this, swapShorts, iters, num_shorts);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapInts_unaligned1, int)->AT_COMMON_VALUES;
+void BM_libcore_swapInts_unaligned1::Run(int iters, int num_ints) {
+  swap_bench<jint, 1>(this, swapInts, iters, num_ints);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapLongs_unaligned1, int)->AT_COMMON_VALUES;
+void BM_libcore_swapLongs_unaligned1::Run(int iters, int num_longs) {
+  swap_bench<jlong, 1>(this, swapLongs, iters, num_longs);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapShorts_unaligned2, int)->AT_COMMON_VALUES;
+void BM_libcore_swapShorts_unaligned2::Run(int iters, int num_shorts) {
+  swap_bench<jshort, 2>(this, swapShorts, iters, num_shorts);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapInts_unaligned2, int)->AT_COMMON_VALUES;
+void BM_libcore_swapInts_unaligned2::Run(int iters, int num_ints) {
+  swap_bench<jint, 2>(this, swapInts, iters, num_ints);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapLongs_unaligned2, int)->AT_COMMON_VALUES;
+void BM_libcore_swapLongs_unaligned2::Run(int iters, int num_longs) {
+  swap_bench<jlong, 2>(this, swapLongs, iters, num_longs);
+}
diff --git a/luni/src/main/files/README.cacerts b/luni/src/main/files/README.cacerts
index e905a68..ca5c570 100755
--- a/luni/src/main/files/README.cacerts
+++ b/luni/src/main/files/README.cacerts
@@ -1,7 +1,7 @@
 The filenames in the cacerts directory are in the format of <hash>.<n>
 where "hash" is the subject hash produced by:
 
-    openssl x509 -subject_hash -in filename
+    openssl x509 -subject_hash_old -in filename
 
 and the "n" is a unique integer identifier starting at 0 to deal
 with collisions. See OpenSSL's c_rehash manpage for details.
diff --git a/luni/src/main/java/android/system/NetlinkSocketAddress.java b/luni/src/main/java/android/system/NetlinkSocketAddress.java
new file mode 100644
index 0000000..af78cd0
--- /dev/null
+++ b/luni/src/main/java/android/system/NetlinkSocketAddress.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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 android.system;
+
+import libcore.util.Objects;
+import java.net.SocketAddress;
+
+/**
+ * Netlink socket address.
+ *
+ * Corresponds to Linux's {@code struct sockaddr_nl} from
+ * <a href="https://github.com/torvalds/linux/blob/master/include/uapi/linux/netlink.h">&lt;linux/netlink.h&gt;</a>.
+ *
+ * @hide
+ */
+public final class NetlinkSocketAddress extends SocketAddress {
+    /** port ID */
+    private final int nlPortId;
+
+    /** multicast groups mask */
+    private final int nlGroupsMask;
+
+    public NetlinkSocketAddress() {
+        this(0, 0);
+    }
+
+    public NetlinkSocketAddress(int nlPortId) {
+        this(nlPortId, 0);
+    }
+
+    public NetlinkSocketAddress(int nlPortId, int nlGroupsMask) {
+        this.nlPortId = nlPortId;
+        this.nlGroupsMask = nlGroupsMask;
+    }
+
+    public int getPortId() {
+        return nlPortId;
+    }
+
+    public int getGroupsMask() {
+        return nlGroupsMask;
+    }
+
+    @Override public String toString() {
+      return Objects.toString(this);
+    }
+}
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index 9d6dc1b..fcecf18 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -54,6 +54,8 @@
    */
   public static void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { Libcore.os.bind(fd, address, port); }
 
+  /** @hide */ public static void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.bind(fd, address); }
+
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/chmod.2.html">chmod(2)</a>.
    */
@@ -74,6 +76,8 @@
    */
   public static void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { Libcore.os.connect(fd, address, port); }
 
+  /** @hide */ public static void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.connect(fd, address); }
+
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/dup.2.html">dup(2)</a>.
    */
@@ -109,9 +113,9 @@
    */
   public static void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { Libcore.os.fchown(fd, uid, gid); }
 
-  /** @hide */ public static int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return Libcore.os.fcntlVoid(fd, cmd); }
-  /** @hide */ public static int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return Libcore.os.fcntlLong(fd, cmd, arg); }
   /** @hide */ public static int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException { return Libcore.os.fcntlFlock(fd, cmd, arg); }
+  /** @hide */ public static int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException { return Libcore.os.fcntlInt(fd, cmd, arg); }
+  /** @hide */ public static int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return Libcore.os.fcntlVoid(fd, cmd); }
 
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/fdatasync.2.html">fdatasync(2)</a>.
@@ -171,6 +175,11 @@
   public static SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return Libcore.os.getpeername(fd); }
 
   /**
+   * See <a href="http://man7.org/linux/man-pages/man2/getpgid.2.html">getpgid(2)</a>.
+   */
+  /** @hide */ public static int getpgid(int pid) throws ErrnoException { return Libcore.os.getpgid(pid); }
+
+  /**
    * See <a href="http://man7.org/linux/man-pages/man2/getpid.2.html">getpid(2)</a>.
    */
   public static int getpid() { return Libcore.os.getpid(); }
@@ -302,10 +311,16 @@
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/pipe.2.html">pipe(2)</a>.
    */
-  public static FileDescriptor[] pipe() throws ErrnoException { return Libcore.os.pipe(); }
+  public static FileDescriptor[] pipe() throws ErrnoException { return Libcore.os.pipe2(0); }
+
+  /** @hide */ public static FileDescriptor[] pipe2(int flags) throws ErrnoException { return Libcore.os.pipe2(flags); }
 
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/poll.2.html">poll(2)</a>.
+   *
+   * <p>Note that in Lollipop this could throw an {@code ErrnoException} with {@code EINTR}.
+   * In later releases, the implementation will automatically just restart the system call with
+   * an appropriately reduced timeout.
    */
   public static int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return Libcore.os.poll(fds, timeoutMs); }
 
@@ -395,6 +410,11 @@
   public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
 
   /**
+   * See <a href="http://man7.org/linux/man-pages/man2/sendto.2.html">sendto(2)</a>.
+   */
+  /** @hide */ public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
+
+  /**
    * See <a href="http://man7.org/linux/man-pages/man2/setegid.2.html">setegid(2)</a>.
    */
   public static void setegid(int egid) throws ErrnoException { Libcore.os.setegid(egid); }
@@ -415,6 +435,21 @@
   public static void setgid(int gid) throws ErrnoException { Libcore.os.setgid(gid); }
 
   /**
+   * See <a href="http://man7.org/linux/man-pages/man2/setpgid.2.html">setpgid(2)</a>.
+   */
+  /** @hide */ public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }
+
+  /**
+   * See <a href="http://man7.org/linux/man-pages/man2/setregid.2.html">setregid(2)</a>.
+   */
+  /** @hide */ public static void setregid(int rgid, int egid) throws ErrnoException { Libcore.os.setregid(rgid, egid); }
+
+  /**
+   * See <a href="http://man7.org/linux/man-pages/man2/setreuid.2.html">setreuid(2)</a>.
+   */
+  /** @hide */ public static void setreuid(int ruid, int euid) throws ErrnoException { Libcore.os.setreuid(ruid, euid); }
+
+  /**
    * See <a href="http://man7.org/linux/man-pages/man2/setsid.2.html">setsid(2)</a>.
    */
   public static int setsid() throws ErrnoException { return Libcore.os.setsid(); }
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index c758eb7..c0d31e5 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -95,6 +95,8 @@
 
     public static final int AF_INET = placeholder();
     public static final int AF_INET6 = placeholder();
+    /** @hide */ public static final int AF_NETLINK = placeholder();
+    /** @hide */ public static final int AF_PACKET = placeholder();
     public static final int AF_UNIX = placeholder();
     public static final int AF_UNSPEC = placeholder();
     public static final int AI_ADDRCONFIG = placeholder();
@@ -104,6 +106,8 @@
     public static final int AI_NUMERICSERV = placeholder();
     public static final int AI_PASSIVE = placeholder();
     public static final int AI_V4MAPPED = placeholder();
+    /** @hide */ public static final int ARPHRD_ETHER = placeholder();
+    /** @hide */ public static final int ARPHRD_LOOPBACK = placeholder();
     public static final int CAP_AUDIT_CONTROL = placeholder();
     public static final int CAP_AUDIT_WRITE = placeholder();
     public static final int CAP_BLOCK_SUSPEND = placeholder();
@@ -227,6 +231,9 @@
     public static final int ESPIPE = placeholder();
     public static final int ESRCH = placeholder();
     public static final int ESTALE = placeholder();
+    /** @hide */ public static final int ETH_P_ARP = placeholder();
+    /** @hide */ public static final int ETH_P_IP = placeholder();
+    /** @hide */ public static final int ETH_P_IPV6 = placeholder();
     public static final int ETIME = placeholder();
     public static final int ETIMEDOUT = placeholder();
     public static final int ETXTBSY = placeholder();
@@ -324,6 +331,7 @@
     public static final int MS_ASYNC = placeholder();
     public static final int MS_INVALIDATE = placeholder();
     public static final int MS_SYNC = placeholder();
+    /** @hide */ public static final int NETLINK_ROUTE = placeholder();
     public static final int NI_DGRAM = placeholder();
     public static final int NI_NAMEREQD = placeholder();
     public static final int NI_NOFQDN = placeholder();
@@ -331,6 +339,7 @@
     public static final int NI_NUMERICSERV = placeholder();
     public static final int O_ACCMODE = placeholder();
     public static final int O_APPEND = placeholder();
+    /** @hide */ public static final int O_CLOEXEC = placeholder();
     public static final int O_CREAT = placeholder();
     public static final int O_EXCL = placeholder();
     public static final int O_NOCTTY = placeholder();
@@ -364,6 +373,19 @@
     public static final int RT_SCOPE_NOWHERE = placeholder();
     public static final int RT_SCOPE_SITE = placeholder();
     public static final int RT_SCOPE_UNIVERSE = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV4_IFADDR = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV4_MROUTE = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV4_ROUTE = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV4_RULE = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV6_IFADDR = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV6_IFINFO = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV6_MROUTE = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV6_PREFIX = placeholder();
+    /** @hide */ public static final int RTMGRP_IPV6_ROUTE = placeholder();
+    /** @hide */ public static final int RTMGRP_LINK = placeholder();
+    /** @hide */ public static final int RTMGRP_NEIGH = placeholder();
+    /** @hide */ public static final int RTMGRP_NOTIFY = placeholder();
+    /** @hide */ public static final int RTMGRP_TC = placeholder();
     public static final int SEEK_CUR = placeholder();
     public static final int SEEK_END = placeholder();
     public static final int SEEK_SET = placeholder();
@@ -433,6 +455,15 @@
     public static final int STDERR_FILENO = placeholder();
     public static final int STDIN_FILENO = placeholder();
     public static final int STDOUT_FILENO = placeholder();
+    /** @hide */ public static final int ST_MANDLOCK = placeholder();
+    /** @hide */ public static final int ST_NOATIME = placeholder();
+    /** @hide */ public static final int ST_NODEV = placeholder();
+    /** @hide */ public static final int ST_NODIRATIME = placeholder();
+    /** @hide */ public static final int ST_NOEXEC = placeholder();
+    /** @hide */ public static final int ST_NOSUID = placeholder();
+    /** @hide */ public static final int ST_RDONLY = placeholder();
+    /** @hide */ public static final int ST_RELATIME = placeholder();
+    /** @hide */ public static final int ST_SYNCHRONOUS = placeholder();
     public static final int S_IFBLK = placeholder();
     public static final int S_IFCHR = placeholder();
     public static final int S_IFDIR = placeholder();
diff --git a/luni/src/main/java/android/system/PacketSocketAddress.java b/luni/src/main/java/android/system/PacketSocketAddress.java
new file mode 100644
index 0000000..510771c
--- /dev/null
+++ b/luni/src/main/java/android/system/PacketSocketAddress.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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 android.system;
+
+import libcore.util.Objects;
+import java.net.SocketAddress;
+
+/**
+ * Packet socket address.
+ *
+ * Corresponds to Linux's {@code struct sockaddr_ll}.
+ *
+ * @hide
+ */
+public final class PacketSocketAddress extends SocketAddress {
+    /** Protocol. An Ethernet protocol type, e.g., {@code ETH_P_IPV6}. */
+    public short sll_protocol;
+
+    /** Interface index. */
+    public int sll_ifindex;
+
+    /** ARP hardware type. One of the {@code ARPHRD_*} constants. */
+    public short sll_hatype;
+
+    /** Packet type. One of the {@code PACKET_*} constants, such as {@code PACKET_OTHERHOST}. */
+    public byte sll_pkttype;
+
+    /** Hardware address. */
+    public byte[] sll_addr;
+
+    /** Constructs a new PacketSocketAddress. */
+    public PacketSocketAddress(short sll_protocol, int sll_ifindex,
+            short sll_hatype, byte sll_pkttype, byte[] sll_addr) {
+        this.sll_protocol = sll_protocol;
+        this.sll_ifindex = sll_ifindex;
+        this.sll_hatype = sll_hatype;
+        this.sll_pkttype = sll_pkttype;
+        this.sll_addr = sll_addr;
+    }
+
+    /** Constructs a new PacketSocketAddress suitable for binding to. */
+    public PacketSocketAddress(short sll_protocol, int sll_ifindex) {
+        this(sll_protocol, sll_ifindex, (short) 0, (byte) 0, null);
+    }
+
+    /** Constructs a new PacketSocketAddress suitable for sending to. */
+    public PacketSocketAddress(int sll_ifindex, byte[] sll_addr) {
+        this((short) 0, sll_ifindex, (short) 0, (byte) 0, sll_addr);
+    }
+}
diff --git a/luni/src/main/java/java/io/BufferedInputStream.java b/luni/src/main/java/java/io/BufferedInputStream.java
index 85236b6..ec43720 100644
--- a/luni/src/main/java/java/io/BufferedInputStream.java
+++ b/luni/src/main/java/java/io/BufferedInputStream.java
@@ -150,7 +150,7 @@
             if (result > 0) {
                 markpos = -1;
                 pos = 0;
-                count = result == -1 ? 0 : result;
+                count = result;
             }
             return result;
         }
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index d107c28..0592345 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -57,12 +57,6 @@
     private static final long serialVersionUID = 301077366599181567L;
 
     /**
-     * Reusing a Random makes temporary filenames slightly harder to predict.
-     * (Random is thread-safe.)
-     */
-    private static final Random tempFileRandom = new Random();
-
-    /**
      * The system-dependent character used to separate components in filenames ('/').
      * Use of this (rather than hard-coding '/') helps portability to other operating systems.
      *
@@ -129,6 +123,8 @@
      *
      * @param path
      *            the path to be used for the file.
+     * @throws NullPointerException
+     *             if {@code path} is {@code null}.
      */
     public File(String path) {
         this.path = fixSlashes(path);
@@ -167,6 +163,8 @@
      * @param uri
      *            the Unified Resource Identifier that is used to construct this
      *            file.
+     * @throws NullPointerException
+     *             if {@code uri == null}.
      * @throws IllegalArgumentException
      *             if {@code uri} does not comply with the conditions above.
      * @see #toURI
@@ -1002,7 +1000,7 @@
         }
         File result;
         do {
-            result = new File(tmpDirFile, prefix + tempFileRandom.nextInt() + suffix);
+            result = new File(tmpDirFile, prefix + Math.randomIntInternal() + suffix);
         } while (!result.createNewFile());
         return result;
     }
diff --git a/luni/src/main/java/java/io/FileDescriptor.java b/luni/src/main/java/java/io/FileDescriptor.java
index eba0e4d..be94c52 100644
--- a/luni/src/main/java/java/io/FileDescriptor.java
+++ b/luni/src/main/java/java/io/FileDescriptor.java
@@ -108,7 +108,7 @@
     /**
      * @hide internal use only
      */
-    public boolean isSocket() {
+    public final boolean isSocket$() {
         return isSocket(descriptor);
     }
 
diff --git a/luni/src/main/java/java/io/InputStreamReader.java b/luni/src/main/java/java/io/InputStreamReader.java
index d57b916..a4cacf2 100644
--- a/luni/src/main/java/java/io/InputStreamReader.java
+++ b/luni/src/main/java/java/io/InputStreamReader.java
@@ -101,7 +101,9 @@
      */
     public InputStreamReader(InputStream in, CharsetDecoder dec) {
         super(in);
-        dec.averageCharsPerByte();
+        if (dec == null) {
+            throw new NullPointerException("dec == null");
+        }
         this.in = in;
         decoder = dec;
         bytes.limit(0);
diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java
index 3a89b52..cd267b2 100644
--- a/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/luni/src/main/java/java/io/ObjectInputStream.java
@@ -1977,7 +1977,7 @@
             // original/outside caller
             if (++nestedLevels == 1) {
                 // Remember the caller's class loader
-                callerClassLoader = VMStack.getClosestUserClassLoader(bootstrapLoader, systemLoader);
+                callerClassLoader = VMStack.getClosestUserClassLoader();
             }
 
             result = readNonPrimitiveContent(unshared);
@@ -2014,9 +2014,6 @@
         return result;
     }
 
-    private static final ClassLoader bootstrapLoader = Object.class.getClassLoader();
-    private static final ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
-
     /**
      * Method to be overridden by subclasses to read the next object from the
      * source stream.
@@ -2258,8 +2255,6 @@
 
             if (cls == null) {
                 // not primitive class
-                // Use the first non-null ClassLoader on the stack. If null, use
-                // the system class loader
                 cls = Class.forName(className, false, callerClassLoader);
             }
         }
diff --git a/luni/src/main/java/java/io/ObjectStreamField.java b/luni/src/main/java/java/io/ObjectStreamField.java
index 78a6903..2a9b107 100644
--- a/luni/src/main/java/java/io/ObjectStreamField.java
+++ b/luni/src/main/java/java/io/ObjectStreamField.java
@@ -58,13 +58,7 @@
      *             if {@code name} or {@code cl} is {@code null}.
      */
     public ObjectStreamField(String name, Class<?> cl) {
-        if (name == null) {
-            throw new NullPointerException("name == null");
-        } else if (cl == null) {
-            throw new NullPointerException("cl == null");
-        }
-        this.name = name;
-        this.type = new WeakReference<Class<?>>(cl);
+        this(name, cl, false);
     }
 
     /**
diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java
index bc8710d..8a639e7 100644
--- a/luni/src/main/java/java/io/OutputStreamWriter.java
+++ b/luni/src/main/java/java/io/OutputStreamWriter.java
@@ -116,7 +116,10 @@
      */
     public OutputStreamWriter(OutputStream out, CharsetEncoder charsetEncoder) {
         super(out);
-        charsetEncoder.charset();
+        if (charsetEncoder == null) {
+            throw new NullPointerException("charsetEncoder == null");
+        }
+
         this.out = out;
         encoder = charsetEncoder;
     }
diff --git a/luni/src/main/java/java/io/RandomAccessFile.java b/luni/src/main/java/java/io/RandomAccessFile.java
index da99765..0e4fa4f 100644
--- a/luni/src/main/java/java/io/RandomAccessFile.java
+++ b/luni/src/main/java/java/io/RandomAccessFile.java
@@ -160,7 +160,6 @@
         synchronized (this) {
             if (channel != null && channel.isOpen()) {
                 channel.close();
-                channel = null;
             }
             IoBridge.closeAndSignalBlockedThreads(fd);
         }
@@ -185,6 +184,10 @@
      * changes made to this file's file pointer offset are also visible in the
      * file channel's position and vice versa.
      *
+     * Closing the channel closes the RandomAccessFile as well. The instance
+     * of FileChannel returned is always the same even if the RandomAccessFile
+     * or the FileChannel have been closed.
+     *
      * @return this file's file channel instance.
      */
     public final synchronized FileChannel getChannel() {
diff --git a/luni/src/main/java/java/lang/Math.java b/luni/src/main/java/java/lang/Math.java
index 86df784..7203566 100644
--- a/luni/src/main/java/java/lang/Math.java
+++ b/luni/src/main/java/java/lang/Math.java
@@ -35,7 +35,9 @@
      */
     public static final double PI = 3.141592653589793;
 
-    private static Random random;
+    private static class NoImagePreloadHolder {
+        private static final Random INSTANCE = new Random();
+    }
 
     /**
      * Prevents this class from being instantiated.
@@ -875,11 +877,25 @@
      *
      * @return a pseudo-random number.
      */
-    public static synchronized double random() {
-        if (random == null) {
-            random = new Random();
-        }
-        return random.nextDouble();
+    public static double random() {
+        return NoImagePreloadHolder.INSTANCE.nextDouble();
+    }
+
+    /**
+     * Set the seed for the pseudo random generator used by {@link #random()}
+     * and {@link #randomIntInternal()}.
+     *
+     * @hide for internal use only.
+     */
+    public static void setRandomSeedInternal(long seed) {
+        NoImagePreloadHolder.INSTANCE.setSeed(seed);
+    }
+
+    /**
+     * @hide for internal use only.
+     */
+    public static int randomIntInternal() {
+        return NoImagePreloadHolder.INSTANCE.nextInt();
     }
 
     /**
diff --git a/luni/src/main/java/java/lang/Package.java b/luni/src/main/java/java/lang/Package.java
index 7e30883..3c6c39c 100644
--- a/luni/src/main/java/java/lang/Package.java
+++ b/luni/src/main/java/java/lang/Package.java
@@ -51,6 +51,7 @@
 public class Package implements AnnotatedElement {
     private static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
 
+    private final ClassLoader classLoader;
     private final String name;
     private final String specTitle;
     private final String specVersion;
@@ -60,8 +61,10 @@
     private final String implVendor;
     private final URL sealBase;
 
-    Package(String name, String specTitle, String specVersion, String specVendor,
-            String implTitle, String implVersion, String implVendor, URL sealBase) {
+    Package(ClassLoader classLoader, String name, String specTitle, String specVersion,
+            String specVendor, String implTitle, String implVersion, String implVendor,
+            URL sealBase) {
+        this.classLoader = classLoader;
         this.name = name;
         this.specTitle = specTitle;
         this.specVersion = specVersion;
@@ -96,7 +99,8 @@
      */
     public Annotation[] getAnnotations() {
         try {
-            Class<?> c = Class.forName(getName() + ".package-info");
+            Class<?> c = Class.forName(getName() + ".package-info", false /* initialize */,
+                    classLoader);
             return c.getAnnotations();
         } catch (Exception ex) {
             return NO_ANNOTATIONS;
@@ -175,11 +179,11 @@
      * @see ClassLoader#getPackage(java.lang.String)
      */
     public static Package getPackage(String packageName) {
-        ClassLoader classloader = VMStack.getCallingClassLoader();
-        if (classloader == null) {
-            classloader = ClassLoader.getSystemClassLoader();
+        ClassLoader classLoader = VMStack.getCallingClassLoader();
+        if (classLoader == null) {
+            classLoader = ClassLoader.getSystemClassLoader();
         }
-        return classloader.getPackage(packageName);
+        return classLoader.getPackage(packageName);
     }
 
     /**
@@ -189,11 +193,11 @@
      * @see ClassLoader#getPackages
      */
     public static Package[] getPackages() {
-        ClassLoader classloader = VMStack.getCallingClassLoader();
-        if (classloader == null) {
-            classloader = ClassLoader.getSystemClassLoader();
+        ClassLoader classLoader = VMStack.getCallingClassLoader();
+        if (classLoader == null) {
+            classLoader = ClassLoader.getSystemClassLoader();
         }
-        return classloader.getPackages();
+        return classLoader.getPackages();
     }
 
     /**
diff --git a/luni/src/main/java/java/lang/Runtime.java b/luni/src/main/java/java/lang/Runtime.java
index a3cb83e..3ddacf7 100644
--- a/luni/src/main/java/java/lang/Runtime.java
+++ b/luni/src/main/java/java/lang/Runtime.java
@@ -357,14 +357,11 @@
      */
     void loadLibrary(String libraryName, ClassLoader loader) {
         if (loader != null) {
+            // TODO: We shouldn't assume that we know default linker search logic.
             String filename = loader.findLibrary(libraryName);
             if (filename == null) {
-                // It's not necessarily true that the ClassLoader used
-                // System.mapLibraryName, but the default setup does, and it's
-                // misleading to say we didn't find "libMyLibrary.so" when we
-                // actually searched for "liblibMyLibrary.so.so".
-                throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
-                                               System.mapLibraryName(libraryName) + "\"");
+                // The dynamic linker might still find the library by name.
+                filename = System.mapLibraryName(libraryName);
             }
             String error = doLoad(filename, loader);
             if (error != null) {
@@ -418,19 +415,27 @@
 
         // So, find out what the native library search path is for the ClassLoader in question...
         String ldLibraryPath = null;
-        if (loader != null && loader instanceof BaseDexClassLoader) {
-            ldLibraryPath = ((BaseDexClassLoader) loader).getLdLibraryPath();
+        String dexPath = null;
+        if (loader == null) {
+            // We use the given library path for the boot class loader. This is the path
+            // also used in loadLibraryName if loader is null.
+            ldLibraryPath = System.getProperty("java.library.path");
+        } else if (loader instanceof BaseDexClassLoader) {
+            BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
+            ldLibraryPath = dexClassLoader.getLdLibraryPath();
+            dexPath = dexClassLoader.getDexPath();
         }
         // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
         // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
         // internal natives.
         synchronized (this) {
-            return nativeLoad(name, loader, ldLibraryPath);
+            return nativeLoad(name, loader, ldLibraryPath, dexPath);
         }
     }
 
     // TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
-    private static native String nativeLoad(String filename, ClassLoader loader, String ldLibraryPath);
+    private static native String nativeLoad(String filename, ClassLoader loader,
+            String ldLibraryPath, String dexPath);
 
     /**
      * Provides a hint to the VM that it would be useful to attempt
diff --git a/luni/src/main/java/java/lang/StrictMath.java b/luni/src/main/java/java/lang/StrictMath.java
index f409c06..2e848f2 100644
--- a/luni/src/main/java/java/lang/StrictMath.java
+++ b/luni/src/main/java/java/lang/StrictMath.java
@@ -15,6 +15,18 @@
  *  limitations under the License.
  */
 
+/*
+ * acos, asin, atan, cosh, sinh, tanh, exp, expm1, log, log10, log1p, and cbrt
+ * have been implemented with the following license.
+ * ====================================================
+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
 package java.lang;
 
 /**
@@ -102,6 +114,21 @@
         return Math.abs(l);
     }
 
+    private static final double PIO2_HI = 1.57079632679489655800e+00;
+    private static final double PIO2_LO = 6.12323399573676603587e-17;
+    private static final double PS0 = 1.66666666666666657415e-01;
+    private static final double PS1 = -3.25565818622400915405e-01;
+    private static final double PS2 = 2.01212532134862925881e-01;
+    private static final double PS3 = -4.00555345006794114027e-02;
+    private static final double PS4 = 7.91534994289814532176e-04;
+    private static final double PS5 = 3.47933107596021167570e-05;
+    private static final double QS1 = -2.40339491173441421878e+00;
+    private static final double QS2 = 2.02094576023350569471e+00;
+    private static final double QS3 = -6.88283971605453293030e-01;
+    private static final double QS4 = 7.70381505559019352791e-02;
+    private static final double HUGE = 1.000e+300;
+    private static final double PIO4_HI = 7.85398163397448278999e-01;
+
     /**
      * Returns the closest double approximation of the arc cosine of the
      * argument within the range {@code [0..pi]}.
@@ -113,11 +140,62 @@
      * <li>{@code acos(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value to compute arc cosine of.
      * @return the arc cosine of the argument.
      */
-    public static native double acos(double d);
+    public static double acos(double x) {
+        double z, p, q, r, w, s, c, df;
+        int hx, ix;
+        final long bits = Double.doubleToRawLongBits(x);
+        hx = (int) (bits >>> 32);
+        ix = hx & 0x7fffffff;
+        if (ix >= 0x3ff00000) { /* |x| >= 1 */
+            if ((((ix - 0x3ff00000) | ((int) bits))) == 0) { /* |x|==1 */
+                if (hx > 0) {
+                    return 0.0; /* ieee_acos(1) = 0 */
+                } else {
+                    return 3.14159265358979311600e+00 + 2.0 * PIO2_LO; /* ieee_acos(-1)= pi */
+                }
+            }
+            return (x - x) / (x - x); /* ieee_acos(|x|>1) is NaN */
+        }
+
+        if (ix < 0x3fe00000) { /* |x| < 0.5 */
+            if (ix <= 0x3c600000) {
+                return PIO2_HI + PIO2_LO;/* if|x|<2**-57 */
+            }
+
+            z = x * x;
+            p = z * (PS0 + z
+                    * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
+            q = 1.00000000000000000000e+00 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
+            r = p / q;
+            return PIO2_HI - (x - (PIO2_LO - x * r));
+        } else if (hx < 0) { /* x < -0.5 */
+            z = (1.00000000000000000000e+00 + x) * 0.5;
+            p = z * (PS0 + z
+                    * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
+            q = 1.00000000000000000000e+00 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
+            s = StrictMath.sqrt(z);
+            r = p / q;
+            w = r * s - PIO2_LO;
+            return 3.14159265358979311600e+00 - 2.0 * (s + w);
+        } else { /* x > 0.5 */
+            z = (1.00000000000000000000e+00 - x) * 0.5;
+            s = StrictMath.sqrt(z);
+            df = s;
+            df = Double.longBitsToDouble(
+                    Double.doubleToRawLongBits(df) & 0xffffffffL << 32);
+            c = (z - df * df) / (s + df);
+            p = z * (PS0 + z
+                    * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
+            q = 1.00000000000000000000e+00 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
+            r = p / q;
+            w = r * s + c;
+            return 2.0 * (df + w);
+        }
+    }
 
     /**
      * Returns the closest double approximation of the arc sine of the argument
@@ -130,11 +208,75 @@
      * <li>{@code asin(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose arc sine has to be computed.
      * @return the arc sine of the argument.
      */
-    public static native double asin(double d);
+    public static double asin(double x) {
+        double t, w, p, q, c, r, s;
+        int hx, ix;
+        final long bits = Double.doubleToRawLongBits(x);
+        hx = (int) (bits >>> 32);
+        ix = hx & 0x7fffffff;
+        if (ix >= 0x3ff00000) { /* |x|>= 1 */
+            if ((((ix - 0x3ff00000) | ((int) bits))) == 0) {
+                /* ieee_asin(1)=+-pi/2 with inexact */
+                return x * PIO2_HI + x * PIO2_LO;
+            }
+            return (x - x) / (x - x); /* ieee_asin(|x|>1) is NaN */
+        } else if (ix < 0x3fe00000) { /* |x|<0.5 */
+            if (ix < 0x3e400000) { /* if |x| < 2**-27 */
+                if (HUGE + x > 1.00000000000000000000e+00) {
+                    return x;/* return x with inexact if x!=0 */
+                }
+            } else {
+                t = x * x;
+                p = t * (PS0 + t
+                        * (PS1 + t * (PS2 + t * (PS3 + t * (PS4 + t * PS5)))));
+                q = 1.00000000000000000000e+00 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4)));
+                w = p / q;
+                return x + x * w;
+            }
+        }
+        /* 1> |x|>= 0.5 */
+        w = 1.00000000000000000000e+00 - Math.abs(x);
+        t = w * 0.5;
+        p = t * (PS0 + t * (PS1 + t * (PS2 + t * (PS3 + t * (PS4 + t * PS5)))));
+        q = 1.00000000000000000000e+00 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4)));
+        s = StrictMath.sqrt(t);
+        if (ix >= 0x3FEF3333) { /* if |x| > 0.975 */
+            w = p / q;
+            t = PIO2_HI - (2.0 * (s + s * w) - PIO2_LO);
+        } else {
+            w = s;
+            w = Double.longBitsToDouble(
+                    Double.doubleToRawLongBits(w) & 0xffffffffL << 32);
+            c = (t - w * w) / (s + w);
+            r = p / q;
+            p = 2.0 * s * r - (PIO2_LO - 2.0 * c);
+            q = PIO4_HI - 2.0 * w;
+            t = PIO4_HI - (p - q);
+        }
+        return (hx > 0) ? t : -t;
+    }
+
+    private static final double[] ATANHI = { 4.63647609000806093515e-01,
+            7.85398163397448278999e-01, 9.82793723247329054082e-01,
+            1.57079632679489655800e+00 };
+    private static final double[] ATANLO = { 2.26987774529616870924e-17,
+            3.06161699786838301793e-17, 1.39033110312309984516e-17,
+            6.12323399573676603587e-17 };
+    private static final double AT0 = 3.33333333333329318027e-01;
+    private static final double AT1 = -1.99999999998764832476e-01;
+    private static final double AT2 = 1.42857142725034663711e-01;
+    private static final double AT3 = -1.11111104054623557880e-01;
+    private static final double AT4 = 9.09088713343650656196e-02;
+    private static final double AT5 = -7.69187620504482999495e-02;
+    private static final double AT6 = 6.66107313738753120669e-02;
+    private static final double AT7= -5.83357013379057348645e-02;
+    private static final double AT8 = 4.97687799461593236017e-02;
+    private static final double AT9 = -3.65315727442169155270e-02;
+    private static final double AT10 = 1.62858201153657823623e-02;
 
     /**
      * Returns the closest double approximation of the arc tangent of the
@@ -149,11 +291,73 @@
      * <li>{@code atan(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose arc tangent has to be computed.
      * @return the arc tangent of the argument.
      */
-    public static native double atan(double d);
+    public static double atan(double x) {
+       double w, s1, s2, z;
+        int ix, hx, id;
+
+        final long bits = Double.doubleToRawLongBits(x);
+        hx = (int) (bits >>> 32);
+        ix = hx & 0x7fffffff;
+        if (ix >= 0x44100000) { /* if |x| >= 2^66 */
+            if (ix > 0x7ff00000 || (ix == 0x7ff00000 && (((int) bits) != 0))) {
+                return x + x; /* NaN */
+            }
+            if (hx > 0) {
+                return ATANHI[3] + ATANLO[3];
+            } else {
+                return -ATANHI[3] - ATANLO[3];
+            }
+        }
+        if (ix < 0x3fdc0000) { /* |x| < 0.4375 */
+            if (ix < 0x3e200000) { /* |x| < 2^-29 */
+                if (HUGE + x > 1.00000000000000000000e+00) {
+                    return x; /* raise inexact */
+                }
+            }
+            id = -1;
+        } else {
+            x = Math.abs(x);
+            if (ix < 0x3ff30000) { /* |x| < 1.1875 */
+                if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */
+                    id = 0;
+                    x = (2.0 * x - 1.00000000000000000000e+00) / (2.0 + x);
+                } else { /* 11/16<=|x|< 19/16 */
+                    id = 1;
+                    x = (x - 1.00000000000000000000e+00) / (x + 1.00000000000000000000e+00);
+                }
+            } else {
+                if (ix < 0x40038000) { /* |x| < 2.4375 */
+                    id = 2;
+                    x = (x - 1.5) / (1.00000000000000000000e+00 + 1.5 * x);
+                } else { /* 2.4375 <= |x| < 2^66 */
+                    id = 3;
+                    x = -1.0 / x;
+                }
+            }
+        }
+
+        /* end of argument reduction */
+        z = x * x;
+        w = z * z;
+        /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
+        s1 = z * (AT0 + w * (AT2 + w
+                * (AT4 + w * (AT6 + w * (AT8 + w * AT10)))));
+        s2 = w * (AT1 + w * (AT3 + w * (AT5 + w * (AT7 + w * AT9))));
+        if (id < 0) {
+            return x - x * (s1 + s2);
+        } else {
+            z = ATANHI[id] - ((x * (s1 + s2) - ATANLO[id]) - x);
+            return (hx < 0) ? -z : z;
+        }
+    }
+
+    private static final double PI_O_4 = 7.8539816339744827900E-01;
+    private static final double PI_O_2 = 1.5707963267948965580E+00;
+    private static final double PI_LO = 1.2246467991473531772E-16;
 
     /**
      * Returns the closest double approximation of the arc tangent of
@@ -192,7 +396,108 @@
      *            the denominator of the value whose atan has to be computed.
      * @return the arc tangent of {@code y/x}.
      */
-    public static native double atan2(double y, double x);
+    public static double atan2(double y, double x) {
+        double z;
+        int k, m, hx, hy, ix, iy;
+        int lx, ly; // watch out, should be unsigned
+
+        final long yBits = Double.doubleToRawLongBits(y);
+        final long xBits = Double.doubleToRawLongBits(x);
+
+        hx = (int) (xBits >>> 32); // __HI(x);
+        ix = hx & 0x7fffffff;
+        lx = (int) xBits; // __LO(x);
+        hy = (int) (yBits >>> 32); // __HI(y);
+        iy = hy & 0x7fffffff;
+        ly = (int) yBits; // __LO(y);
+        if (((ix | ((lx | -lx) >> 31)) > 0x7ff00000)
+                || ((iy | ((ly | -ly) >> 31)) > 0x7ff00000)) { /* x or y is NaN */
+            return x + y;
+        }
+        if ((hx - 0x3ff00000 | lx) == 0) {
+            return StrictMath.atan(y); /* x=1.0 */
+        }
+
+        m = ((hy >> 31) & 1) | ((hx >> 30) & 2); /* 2*sign(x)+sign(y) */
+
+        /* when y = 0 */
+        if ((iy | ly) == 0) {
+            switch (m) {
+                case 0:
+                case 1:
+                    return y; /* ieee_atan(+-0,+anything)=+-0 */
+                case 2:
+                    return 3.14159265358979311600e+00 + TINY;/* ieee_atan(+0,-anything) = pi */
+                case 3:
+                    return -3.14159265358979311600e+00 - TINY;/* ieee_atan(-0,-anything) =-pi */
+            }
+        }
+        /* when x = 0 */
+        if ((ix | lx) == 0)
+            return (hy < 0) ? -PI_O_2 - TINY : PI_O_2 + TINY;
+
+        /* when x is INF */
+        if (ix == 0x7ff00000) {
+            if (iy == 0x7ff00000) {
+                switch (m) {
+                    case 0:
+                        return PI_O_4 + TINY;/* ieee_atan(+INF,+INF) */
+                    case 1:
+                        return -PI_O_4 - TINY;/* ieee_atan(-INF,+INF) */
+                    case 2:
+                        return 3.0 * PI_O_4 + TINY;/* ieee_atan(+INF,-INF) */
+                    case 3:
+                        return -3.0 * PI_O_4 - TINY;/* ieee_atan(-INF,-INF) */
+                }
+            } else {
+                switch (m) {
+                    case 0:
+                        return 0.0; /* ieee_atan(+...,+INF) */
+                    case 1:
+                        return -0.0; /* ieee_atan(-...,+INF) */
+                    case 2:
+                        return 3.14159265358979311600e+00 + TINY; /* ieee_atan(+...,-INF) */
+                    case 3:
+                        return -3.14159265358979311600e+00 - TINY; /* ieee_atan(-...,-INF) */
+                }
+            }
+        }
+        /* when y is INF */
+        if (iy == 0x7ff00000)
+            return (hy < 0) ? -PI_O_2 - TINY : PI_O_2 + TINY;
+
+        /* compute y/x */
+        k = (iy - ix) >> 20;
+        if (k > 60) {
+            z = PI_O_2 + 0.5 * PI_LO; /* |y/x| > 2**60 */
+        } else if (hx < 0 && k < -60) {
+            z = 0.0; /* |y|/x < -2**60 */
+        } else {
+            z = StrictMath.atan(Math.abs(y / x)); /* safe to do y/x */
+        }
+
+        switch (m) {
+            case 0:
+                return z; /* ieee_atan(+,+) */
+            case 1:
+                // __HI(z) ^= 0x80000000;
+                z = Double.longBitsToDouble(
+                        Double.doubleToRawLongBits(z) ^ (0x80000000L << 32));
+                return z; /* ieee_atan(-,+) */
+            case 2:
+                return 3.14159265358979311600e+00 - (z - PI_LO);/* ieee_atan(+,-) */
+            default: /* case 3 */
+                return (z - PI_LO) - 3.14159265358979311600e+00;/* ieee_atan(-,-) */
+        }
+    }
+
+    private static final int B1 = 715094163;
+    private static final int B2 = 696219795;
+    private static final double C = 5.42857142857142815906e-01;
+    private static final double D = -7.05306122448979611050e-01;
+    private static final double CBRTE = 1.41428571428571436819e+00;
+    private static final double F = 1.60714285714285720630e+00;
+    private static final double G = 3.57142857142857150787e-01;
 
     /**
      * Returns the closest double approximation of the cube root of the
@@ -207,11 +512,79 @@
      * <li>{@code cbrt(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose cube root has to be computed.
      * @return the cube root of the argument.
      */
-    public static native double cbrt(double d);
+    public static double cbrt(double x) {
+        if (x < 0) {
+            return -cbrt(-x);
+        }
+        int hx;
+        double r, s, w;
+        int sign; // caution: should be unsigned
+        long bits = Double.doubleToRawLongBits(x);
+
+        hx = (int) (bits >>> 32);
+        sign = hx & 0x80000000; /* sign= sign(x) */
+        hx ^= sign;
+        if (hx >= 0x7ff00000) {
+            return (x + x); /* ieee_cbrt(NaN,INF) is itself */
+        }
+
+        if ((hx | ((int) bits)) == 0) {
+            return x; /* ieee_cbrt(0) is itself */
+        }
+
+        // __HI(x) = hx; /* x <- |x| */
+        bits &= 0x00000000ffffffffL;
+        bits |= ((long) hx << 32);
+
+        long tBits = Double.doubleToRawLongBits(0.0) & 0x00000000ffffffffL;
+        double t = 0.0;
+        /* rough cbrt to 5 bits */
+        if (hx < 0x00100000) { /* subnormal number */
+            // __HI(t)=0x43500000; /*set t= 2**54*/
+            tBits |= 0x43500000L << 32;
+            t = Double.longBitsToDouble(tBits);
+            t *= x;
+
+            // __HI(t)=__HI(t)/3+B2;
+            tBits = Double.doubleToRawLongBits(t);
+            long tBitsHigh = tBits >> 32;
+            tBits &= 0x00000000ffffffffL;
+            tBits |= ((tBitsHigh / 3) + B2) << 32;
+            t = Double.longBitsToDouble(tBits);
+
+        } else {
+            // __HI(t)=hx/3+B1;
+            tBits |= ((long) ((hx / 3) + B1)) << 32;
+            t = Double.longBitsToDouble(tBits);
+        }
+
+        /* new cbrt to 23 bits, may be implemented in single precision */
+        r = t * t / x;
+        s = C + r * t;
+        t *= G + F / (s + CBRTE + D / s);
+
+        /* chopped to 20 bits and make it larger than ieee_cbrt(x) */
+        tBits = Double.doubleToRawLongBits(t);
+        tBits &= 0xFFFFFFFFL << 32;
+        tBits += 0x00000001L << 32;
+        t = Double.longBitsToDouble(tBits);
+
+        /* one step newton iteration to 53 bits with error less than 0.667 ulps */
+        s = t * t; /* t*t is exact */
+        r = x / s;
+        w = t + t;
+        r = (r - t) / (w + r); /* r-s is exact */
+        t = t + t * r;
+
+        /* retore the sign bit */
+        tBits = Double.doubleToRawLongBits(t);
+        tBits |= ((long) sign) << 32;
+        return Double.longBitsToDouble(tBits);
+    }
 
     /**
      * Returns the double conversion of the most negative (closest to negative
@@ -229,6 +602,8 @@
      */
     public static native double ceil(double d);
 
+    private static final long ONEBITS = Double.doubleToRawLongBits(1.00000000000000000000e+00)
+            & 0x00000000ffffffffL;
 
     /**
      * Returns the closest double approximation of the hyperbolic cosine of the
@@ -241,11 +616,54 @@
      * <li>{@code cosh(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose hyperbolic cosine has to be computed.
      * @return the hyperbolic cosine of the argument.
      */
-    public static native double cosh(double d);
+    public static double cosh(double x) {
+        double t, w;
+        int ix;
+        final long bits = Double.doubleToRawLongBits(x);
+        ix = (int) (bits >>> 32) & 0x7fffffff;
+
+        /* x is INF or NaN */
+        if (ix >= 0x7ff00000) {
+            return x * x;
+        }
+
+        /* |x| in [0,0.5*ln2], return 1+ieee_expm1(|x|)^2/(2*ieee_exp(|x|)) */
+        if (ix < 0x3fd62e43) {
+            t = expm1(Math.abs(x));
+            w = 1.00000000000000000000e+00 + t;
+            if (ix < 0x3c800000)
+                return w; /* ieee_cosh(tiny) = 1 */
+            return 1.00000000000000000000e+00 + (t * t) / (w + w);
+        }
+
+        /* |x| in [0.5*ln2,22], return (ieee_exp(|x|)+1/ieee_exp(|x|)/2; */
+        if (ix < 0x40360000) {
+            t = exp(Math.abs(x));
+            return 0.5 * t + 0.5 / t;
+        }
+
+        /* |x| in [22, ieee_log(maxdouble)] return half*ieee_exp(|x|) */
+        if (ix < 0x40862E42) {
+            return 0.5 * exp(Math.abs(x));
+        }
+
+        /* |x| in [log(maxdouble), overflowthresold] */
+        final long lx = ((ONEBITS >>> 29) + ((int) bits)) & 0x00000000ffffffffL;
+        // watch out: lx should be an unsigned int
+        // lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x);
+        if (ix < 0x408633CE || (ix == 0x408633ce) && (lx <= 0x8fb9f87dL)) {
+            w = exp(0.5 * Math.abs(x));
+            t = 0.5 * w;
+            return t * w;
+        }
+
+        /* |x| > overflowthresold, ieee_cosh(x) overflow */
+        return HUGE * HUGE;
+    }
 
     /**
      * Returns the closest double approximation of the cosine of the argument.
@@ -263,6 +681,19 @@
      */
     public static native double cos(double d);
 
+    private static final double TWON24 = 5.96046447753906250000e-08;
+    private static final double TWO54 = 1.80143985094819840000e+16,
+            TWOM54 = 5.55111512312578270212e-17;
+    private static final double TWOM1000 = 9.33263618503218878990e-302;
+    private static final double O_THRESHOLD = 7.09782712893383973096e+02;
+    private static final double U_THRESHOLD = -7.45133219101941108420e+02;
+    private static final double INVLN2 = 1.44269504088896338700e+00;
+    private static final double P1 = 1.66666666666666019037e-01;
+    private static final double P2 = -2.77777777770155933842e-03;
+    private static final double P3 = 6.61375632143793436117e-05;
+    private static final double P4 = -1.65339022054652515390e-06;
+    private static final double P5 = 4.13813679705723846039e-08;
+
     /**
      * Returns the closest double approximation of the raising "e" to the power
      * of the argument.
@@ -274,11 +705,88 @@
      * <li>{@code exp(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose exponential has to be computed.
      * @return the exponential of the argument.
      */
-    public static native double exp(double d);
+    public static double exp(double x) {
+        double y, c, t;
+        double hi = 0, lo = 0;
+        int k = 0, xsb;
+        int hx; // should be unsigned, be careful!
+        final long bits = Double.doubleToRawLongBits(x);
+        int lowBits = (int) bits;
+        int highBits = (int) (bits >>> 32);
+        hx = highBits & 0x7fffffff;
+        xsb = (highBits >>> 31) & 1;
+
+        /* filter out non-finite argument */
+        if (hx >= 0x40862E42) { /* if |x|>=709.78... */
+            if (hx >= 0x7ff00000) {
+                if (((hx & 0xfffff) | lowBits) != 0) {
+                    return x + x; /* NaN */
+                } else {
+                    return (xsb == 0) ? x : 0.0; /* ieee_exp(+-inf)={inf,0} */
+                }
+            }
+
+            if (x > O_THRESHOLD) {
+                return HUGE * HUGE; /* overflow */
+            }
+
+            if (x < U_THRESHOLD) {
+                return TWOM1000 * TWOM1000; /* underflow */
+            }
+        }
+
+        /* argument reduction */
+        if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */
+            if (hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */
+                hi = x - ((xsb == 0) ? 6.93147180369123816490e-01 :
+                        -6.93147180369123816490e-01); // LN2HI[xsb];
+                lo = (xsb == 0) ? 1.90821492927058770002e-10 :
+                        -1.90821492927058770002e-10; // LN2LO[xsb];
+                k = 1 - xsb - xsb;
+            } else {
+                k = (int) (INVLN2 * x + ((xsb == 0) ? 0.5 : -0.5 ));//halF[xsb]);
+                t = k;
+                hi = x - t * 6.93147180369123816490e-01; //ln2HI[0]; /* t*ln2HI is exact here */
+                lo = t * 1.90821492927058770002e-10; //ln2LO[0];
+            }
+            x = hi - lo;
+        } else if (hx < 0x3e300000) { /* when |x|<2**-28 */
+            if (HUGE + x > 1.00000000000000000000e+00)
+                return 1.00000000000000000000e+00 + x;/* trigger inexact */
+        } else {
+            k = 0;
+        }
+
+        /* x is now in primary range */
+        t = x * x;
+        c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5))));
+        if (k == 0) {
+            return 1.00000000000000000000e+00 - ((x * c) / (c - 2.0) - x);
+        } else {
+            y = 1.00000000000000000000e+00 - ((lo - (x * c) / (2.0 - c)) - hi);
+        }
+        long yBits = Double.doubleToRawLongBits(y);
+        if (k >= -1021) {
+            yBits += ((long) (k << 20)) << 32; /* add k to y's exponent */
+            return Double.longBitsToDouble(yBits);
+        } else {
+            yBits += ((long) ((k + 1000) << 20)) << 32;/* add k to y's exponent */
+            return Double.longBitsToDouble(yBits) * TWOM1000;
+        }
+    }
+
+    private static final double TINY = 1.0e-300;
+    private static final double LN2_HI = 6.93147180369123816490e-01;
+    private static final double LN2_LO = 1.90821492927058770002e-10;
+    private static final double Q1 = -3.33333333333331316428e-02;
+    private static final double Q2 = 1.58730158725481460165e-03;
+    private static final double Q3 = -7.93650757867487942473e-05;
+    private static final double Q4 = 4.00821782732936239552e-06;
+    private static final double Q5 = -2.01099218183624371326e-07;
 
     /**
      * Returns the closest double approximation of <i>{@code e}</i><sup>
@@ -295,17 +803,124 @@
      * <li>{@code expm1(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value to compute the <i>{@code e}</i><sup>{@code d}</sup>
      *            {@code - 1} of.
-     * @return the <i>{@code e}</i><sup>{@code d}</sup>{@code - 1} value
-     *         of the argument.
+     * @return the <i>{@code e}</i><sup>{@code d}</sup>{@code - 1} value of the
+     *         argument.
      */
-    public static native double expm1(double d);
+    public static double expm1(double x) {
+        double y, hi, lo, t, e, hxs, hfx, r1, c = 0.0;
+        int k, xsb;
+        long yBits = 0;
+        final long bits = Double.doubleToRawLongBits(x);
+        int highBits = (int) (bits >>> 32);
+        int lowBits = (int) (bits);
+        int hx = highBits & 0x7fffffff; // caution: should be unsigned!
+        xsb = highBits & 0x80000000; /* sign bit of x */
+        y = xsb == 0 ? x : -x; /* y = |x| */
+
+        /* filter out huge and non-finite argument */
+        if (hx >= 0x4043687A) { /* if |x|>=56*ln2 */
+            if (hx >= 0x40862E42) { /* if |x|>=709.78... */
+                if (hx >= 0x7ff00000) {
+                    if (((hx & 0xfffff) | lowBits) != 0) {
+                        return x + x; /* NaN */
+                    } else {
+                        return (xsb == 0) ? x : -1.0;/* ieee_exp(+-inf)={inf,-1} */
+                    }
+                }
+                if (x > O_THRESHOLD) {
+                    return HUGE * HUGE; /* overflow */
+                }
+            }
+            if (xsb != 0) { /* x < -56*ln2, return -1.0 with inexact */
+                if (x + TINY < 0.0) { /* raise inexact */
+                    return TINY - 1.00000000000000000000e+00; /* return -1 */
+                }
+            }
+        }
+        /* argument reduction */
+        if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */
+            if (hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */
+                if (xsb == 0) {
+                    hi = x - LN2_HI;
+                    lo = LN2_LO;
+                    k = 1;
+                } else {
+                    hi = x + LN2_HI;
+                    lo = -LN2_LO;
+                    k = -1;
+                }
+            } else {
+                k = (int) (INVLN2 * x + ((xsb == 0) ? 0.5 : -0.5));
+                t = k;
+                hi = x - t * LN2_HI; /* t*ln2_hi is exact here */
+                lo = t * LN2_LO;
+            }
+            x = hi - lo;
+            c = (hi - x) - lo;
+        } else if (hx < 0x3c900000) { /* when |x|<2**-54, return x */
+            // t = huge+x; /* return x with inexact flags when x!=0 */
+            // return x - (t-(huge+x));
+            return x; // inexact flag is not set, but Java ignors this flag
+                      // anyway
+        } else {
+            k = 0;
+        }
+
+        /* x is now in primary range */
+        hfx = 0.5 * x;
+        hxs = x * hfx;
+        r1 = 1.00000000000000000000e+00 + hxs * (Q1 + hxs * (Q2 + hxs * (Q3 + hxs * (Q4 + hxs * Q5))));
+        t = 3.0 - r1 * hfx;
+        e = hxs * ((r1 - t) / (6.0 - x * t));
+        if (k == 0) {
+            return x - (x * e - hxs); /* c is 0 */
+        } else {
+            e = (x * (e - c) - c);
+            e -= hxs;
+            if (k == -1) {
+                return 0.5 * (x - e) - 0.5;
+            }
+
+            if (k == 1) {
+                if (x < -0.25) {
+                    return -2.0 * (e - (x + 0.5));
+                } else {
+                    return 1.00000000000000000000e+00 + 2.0 * (x - e);
+                }
+            }
+
+            if (k <= -2 || k > 56) { /* suffice to return ieee_exp(x)-1 */
+                y = 1.00000000000000000000e+00 - (e - x);
+                yBits = Double.doubleToRawLongBits(y);
+                yBits += (((long) k) << 52); /* add k to y's exponent */
+                return Double.longBitsToDouble(yBits) - 1.00000000000000000000e+00;
+            }
+
+            long tBits = Double.doubleToRawLongBits(1.00000000000000000000e+00) & 0x00000000ffffffffL;
+
+            if (k < 20) {
+                tBits |= (((long) 0x3ff00000) - (0x200000 >> k)) << 32;
+                y = Double.longBitsToDouble(tBits) - (e - x);
+                yBits = Double.doubleToRawLongBits(y);
+                yBits += (((long) k) << 52); /* add k to y's exponent */
+                return Double.longBitsToDouble(yBits);
+            } else {
+                tBits |= ((((long) 0x3ff) - k) << 52); /* 2^-k */
+                y = x - (e + Double.longBitsToDouble(tBits));
+                y += 1.00000000000000000000e+00;
+                yBits = Double.doubleToRawLongBits(y);
+                yBits += (((long) k) << 52); /* add k to y's exponent */
+                return Double.longBitsToDouble(yBits);
+            }
+        }
+    }
 
     /**
-     * Returns the double conversion of the most positive (closest to
-     * positive infinity) integer less than or equal to the argument.
+     * Returns the double conversion of the most positive (closest to positive
+     * infinity) integer less than or equal to the argument.
      * <p>
      * Special cases:
      * <ul>
@@ -319,9 +934,9 @@
     public static native double floor(double d);
 
     /**
-     * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +}
-     * <i> {@code y}</i><sup>{@code 2}</sup>{@code )}. The final result is
-     * without medium underflow or overflow.
+     * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
+     * {@code y}</i><sup>{@code 2}</sup>{@code )}. The final result is without
+     * medium underflow or overflow.
      * <p>
      * Special cases:
      * <ul>
@@ -369,6 +984,14 @@
      */
     public static native double IEEEremainder(double x, double y);
 
+    private static final double LG1 = 6.666666666666735130e-01;
+    private static final double LG2 = 3.999999999940941908e-01;
+    private static final double LG3 = 2.857142874366239149e-01;
+    private static final double LG4 = 2.222219843214978396e-01;
+    private static final double LG5 = 1.818357216161805012e-01;
+    private static final double LG6 = 1.531383769920937332e-01;
+    private static final double LG7 = 1.479819860511658591e-01;
+
     /**
      * Returns the closest double approximation of the natural logarithm of the
      * argument.
@@ -383,11 +1006,95 @@
      * <li>{@code log(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose log has to be computed.
      * @return the natural logarithm of the argument.
      */
-    public static native double log(double d);
+    public static double log(double x) {
+        double hfsq, f, s, z, R, w, t1, t2, dk;
+        int hx, i, j, k = 0;
+        int lx; // watch out, should be unsigned
+
+        long bits = Double.doubleToRawLongBits(x);
+        hx = (int) (bits >>> 32); /* high word of x */
+        lx = (int) bits; /* low word of x */
+
+        if (hx < 0x00100000) { /* x < 2**-1022 */
+            if (((hx & 0x7fffffff) | lx) == 0) {
+                return -TWO54 / 0.0; /* ieee_log(+-0)=-inf */
+            }
+
+            if (hx < 0) {
+                return (x - x) / 0.0; /* ieee_log(-#) = NaN */
+            }
+
+            k -= 54;
+            x *= TWO54; /* subnormal number, scale up x */
+            bits = Double.doubleToRawLongBits(x);
+            hx = (int) (bits >>> 32); /* high word of x */
+        }
+
+        if (hx >= 0x7ff00000) {
+            return x + x;
+        }
+
+        k += (hx >> 20) - 1023;
+        hx &= 0x000fffff;
+        bits &= 0x00000000ffffffffL;
+        i = (hx + 0x95f64) & 0x100000;
+        bits |= ((long) hx | (i ^ 0x3ff00000)) << 32; /* normalize x or x/2 */
+        x = Double.longBitsToDouble(bits);
+        k += (i >> 20);
+        f = x - 1.0;
+
+        if ((0x000fffff & (2 + hx)) < 3) { /* |f| < 2**-20 */
+            if (f == 0.0) {
+                if (k == 0) {
+                    return 0.0;
+                } else {
+                    dk = k;
+                }
+                return dk * LN2_HI + dk * LN2_LO;
+            }
+
+            R = f * f * (0.5 - 0.33333333333333333 * f);
+            if (k == 0) {
+                return f - R;
+            } else {
+                dk = k;
+                return dk * LN2_HI - ((R - dk * LN2_LO) - f);
+            }
+        }
+        s = f / (2.0 + f);
+        dk = k;
+        z = s * s;
+        i = hx - 0x6147a;
+        w = z * z;
+        j = 0x6b851 - hx;
+        t1 = w * (LG2 + w * (LG4 + w * LG6));
+        t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7)));
+        i |= j;
+        R = t2 + t1;
+        if (i > 0) {
+            hfsq = 0.5 * f * f;
+            if (k == 0) {
+                return f - (hfsq - s * (hfsq + R));
+            } else {
+                return dk * LN2_HI
+                        - ((hfsq - (s * (hfsq + R) + dk * LN2_LO)) - f);
+            }
+        } else {
+            if (k == 0) {
+                return f - s * (f - R);
+            } else {
+                return dk * LN2_HI - ((s * (f - R) - dk * LN2_LO) - f);
+            }
+        }
+    }
+
+    private static final double IVLN10 = 4.34294481903251816668e-01;
+    private static final double LOG10_2HI = 3.01029995663611771306e-01;
+    private static final double LOG10_2LO = 3.69423907715893078616e-13;
 
     /**
      * Returns the closest double approximation of the base 10 logarithm of the
@@ -403,11 +1110,54 @@
      * <li>{@code log10(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose base 10 log has to be computed.
-     * @return the natural logarithm of the argument.
+     * @return the the base 10 logarithm of x
      */
-    public static native double log10(double d);
+    public static double log10(double x) {
+        double y, z;
+        int i, k = 0, hx;
+        int lx; // careful: lx should be unsigned!
+        long bits = Double.doubleToRawLongBits(x);
+        hx = (int) (bits >> 32); /* high word of x */
+        lx = (int) bits; /* low word of x */
+        if (hx < 0x00100000) { /* x < 2**-1022 */
+            if (((hx & 0x7fffffff) | lx) == 0) {
+                return -TWO54 / 0.0; /* ieee_log(+-0)=-inf */
+            }
+
+            if (hx < 0) {
+                return (x - x) / 0.0; /* ieee_log(-#) = NaN */
+            }
+
+            k -= 54;
+            x *= TWO54; /* subnormal number, scale up x */
+            bits = Double.doubleToRawLongBits(x);
+            hx = (int) (bits >> 32); /* high word of x */
+        }
+
+        if (hx >= 0x7ff00000) {
+            return x + x;
+        }
+
+        k += (hx >> 20) - 1023;
+        i = (int) (((k & 0x00000000ffffffffL) & 0x80000000) >>> 31);
+        hx = (hx & 0x000fffff) | ((0x3ff - i) << 20);
+        y = k + i;
+        bits &= 0x00000000ffffffffL;
+        bits |= ((long) hx) << 32;
+        x = Double.longBitsToDouble(bits); // __HI(x) = hx;
+        z = y * LOG10_2LO + IVLN10 * log(x);
+        return z + y * LOG10_2HI;
+    }
+
+    private static final double LP1 = 6.666666666666735130e-01,
+            LP2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
+            LP3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
+            LP4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
+            LP5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
+            LP6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
+            LP7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
 
     /**
      * Returns the closest double approximation of the natural logarithm of the
@@ -426,11 +1176,107 @@
      * <li>{@code log1p(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value to compute the {@code ln(1+d)} of.
      * @return the natural logarithm of the sum of the argument and 1.
      */
-    public static native double log1p(double d);
+
+    public static double log1p(double x) {
+        double hfsq, f = 0.0, c = 0.0, s, z, R, u = 0.0;
+        int k, hx, hu = 0, ax;
+
+        final long bits = Double.doubleToRawLongBits(x);
+        hx = (int) (bits >>> 32); /* high word of x */
+        ax = hx & 0x7fffffff;
+
+        k = 1;
+        if (hx < 0x3FDA827A) { /* x < 0.41422 */
+            if (ax >= 0x3ff00000) { /* x <= -1.0 */
+                if (x == -1.0) {
+                    return -TWO54 / 0.0; /* ieee_log1p(-1)=+inf */
+                } else {
+                    return (x - x) / (x - x); /* ieee_log1p(x<-1)=NaN */
+                }
+            }
+            if (ax < 0x3e200000) {
+                if (TWO54 + x > 0.0 && ax < 0x3c900000) {
+                    return x;
+                } else {
+                    return x - x * x * 0.5;
+                }
+            }
+            if (hx > 0 || hx <= 0xbfd2bec3) {
+                k = 0;
+                f = x;
+                hu = 1;
+            } /* -0.2929<x<0.41422 */
+        }
+
+        if (hx >= 0x7ff00000) {
+            return x + x;
+        }
+
+        if (k != 0) {
+            long uBits;
+            if (hx < 0x43400000) {
+                u = 1.0 + x;
+                uBits = Double.doubleToRawLongBits(u);
+                hu = (int) (uBits >>> 32);
+                k = (hu >> 20) - 1023;
+                c = (k > 0) ? 1.0 - (u - x) : x - (u - 1.0);/* correction term */
+                c /= u;
+            } else {
+                uBits = Double.doubleToRawLongBits(x);
+                hu = (int) (uBits >>> 32);
+                k = (hu >> 20) - 1023;
+                c = 0;
+            }
+            hu &= 0x000fffff;
+            if (hu < 0x6a09e) {
+                // __HI(u) = hu|0x3ff00000; /* normalize u */
+                uBits &= 0x00000000ffffffffL;
+                uBits |= ((long) hu | 0x3ff00000) << 32;
+                u = Double.longBitsToDouble(uBits);
+            } else {
+                k += 1;
+                // __HI(u) = hu|0x3fe00000; /* normalize u/2 */
+                uBits &= 0xffffffffL;
+                uBits |= ((long) hu | 0x3fe00000) << 32;
+                u = Double.longBitsToDouble(uBits);
+                hu = (0x00100000 - hu) >> 2;
+            }
+            f = u - 1.0;
+        }
+        hfsq = 0.5 * f * f;
+        if (hu == 0) { /* |f| < 2**-20 */
+            if (f == 0.0) {
+                if (k == 0) {
+                    return 0.0;
+                } else {
+                    c += k * LN2_LO;
+                    return k * LN2_HI + c;
+                }
+            }
+
+            R = hfsq * (1.0 - 0.66666666666666666 * f);
+            if (k == 0) {
+                return f - R;
+            } else {
+                return k * LN2_HI - ((R - (k * LN2_LO + c)) - f);
+            }
+        }
+
+        s = f / (2.0 + f);
+        z = s * s;
+        R = z * (LP1 + z * (LP2 + z
+                * (LP3 + z * (LP4 + z * (LP5 + z * (LP6 + z * LP7))))));
+        if (k == 0) {
+            return f - (hfsq - s * (hfsq + R));
+        } else {
+            return k * LN2_HI
+                    - ((hfsq - (s * (hfsq + R) + (k * LN2_LO + c))) - f);
+        }
+    }
 
     /**
      * Returns the most positive (closest to positive infinity) of the two
@@ -453,8 +1299,8 @@
         if (d1 != d2)
             return Double.NaN;
         /* max( +0.0,-0.0) == +0.0 */
-        if (d1 == 0.0
-                && ((Double.doubleToLongBits(d1) & Double.doubleToLongBits(d2)) & 0x8000000000000000L) == 0)
+        if (d1 == 0.0 &&
+                ((Double.doubleToLongBits(d1) & Double.doubleToLongBits(d2)) & 0x8000000000000000L) == 0)
             return 0.0;
         return d1;
     }
@@ -480,8 +1326,8 @@
         if (f1 != f2)
             return Float.NaN;
         /* max( +0.0,-0.0) == +0.0 */
-        if (f1 == 0.0f
-                && ((Float.floatToIntBits(f1) & Float.floatToIntBits(f2)) & 0x80000000) == 0)
+        if (f1 == 0.0f &&
+                ((Float.floatToIntBits(f1) & Float.floatToIntBits(f2)) & 0x80000000) == 0)
             return 0.0f;
         return f1;
     }
@@ -523,8 +1369,8 @@
         if (d1 != d2)
             return Double.NaN;
         /* min( +0.0,-0.0) == -0.0 */
-        if (d1 == 0.0
-                && ((Double.doubleToLongBits(d1) | Double.doubleToLongBits(d2)) & 0x8000000000000000l) != 0)
+        if (d1 == 0.0 &&
+                ((Double.doubleToLongBits(d1) | Double.doubleToLongBits(d2)) & 0x8000000000000000l) != 0)
             return 0.0 * (-1.0);
         return d1;
     }
@@ -550,8 +1396,8 @@
         if (f1 != f2)
             return Float.NaN;
         /* min( +0.0,-0.0) == -0.0 */
-        if (f1 == 0.0f
-                && ((Float.floatToIntBits(f1) | Float.floatToIntBits(f2)) & 0x80000000) != 0)
+        if (f1 == 0.0f &&
+                ((Float.floatToIntBits(f1) | Float.floatToIntBits(f2)) & 0x80000000) != 0)
             return 0.0f * (-1.0f);
         return f1;
     }
@@ -706,7 +1552,7 @@
      *            the value whose signum has to be computed.
      * @return the value of the signum function.
      */
-    public static double signum(double d){
+    public static double signum(double d) {
         return Math.signum(d);
     }
 
@@ -729,10 +1575,12 @@
      *            the value whose signum has to be computed.
      * @return the value of the signum function.
      */
-    public static float signum(float f){
+    public static float signum(float f) {
         return Math.signum(f);
     }
 
+    private static final double shuge = 1.0e307;
+
     /**
      * Returns the closest double approximation of the hyperbolic sine of the
      * argument.
@@ -746,11 +1594,57 @@
      * <li>{@code sinh(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose hyperbolic sine has to be computed.
      * @return the hyperbolic sine of the argument.
      */
-    public static native double sinh(double d);
+    public static double sinh(double x) {
+        double t, w, h;
+        int ix, jx;
+        final long bits = Double.doubleToRawLongBits(x);
+
+        jx = (int) (bits >>> 32);
+        ix = jx & 0x7fffffff;
+
+        /* x is INF or NaN */
+        if (ix >= 0x7ff00000) {
+            return x + x;
+        }
+
+        h = 0.5;
+        if (jx < 0) {
+            h = -h;
+        }
+
+        /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */
+        if (ix < 0x40360000) { /* |x|<22 */
+            if (ix < 0x3e300000) /* |x|<2**-28 */
+                if (shuge + x > 1.00000000000000000000e+00) {
+                    return x;/* ieee_sinh(tiny) = tiny with inexact */
+                }
+            t = expm1(Math.abs(x));
+            if (ix < 0x3ff00000)
+                return h * (2.0 * t - t * t / (t + 1.00000000000000000000e+00));
+            return h * (t + t / (t + 1.00000000000000000000e+00));
+        }
+
+        /* |x| in [22, ieee_log(maxdouble)] return 0.5*ieee_exp(|x|) */
+        if (ix < 0x40862E42) {
+            return h * exp(Math.abs(x));
+        }
+
+        /* |x| in [log(maxdouble), overflowthresold] */
+        final long lx = ((ONEBITS >>> 29) + ((int) bits)) & 0x00000000ffffffffL;
+        // lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x);
+        if (ix < 0x408633CE || (ix == 0x408633ce) && (lx <= 0x8fb9f87dL)) {
+            w = exp(0.5 * Math.abs(x));
+            t = h * w;
+            return t * w;
+        }
+
+        /* |x| > overflowthresold, ieee_sinh(x) overflow */
+        return x * shuge;
+    }
 
     /**
      * Returns the closest double approximation of the sine of the argument.
@@ -816,11 +1710,47 @@
      * <li>{@code tanh(NaN) = NaN}</li>
      * </ul>
      *
-     * @param d
+     * @param x
      *            the value whose hyperbolic tangent has to be computed.
      * @return the hyperbolic tangent of the argument
      */
-    public static native double tanh(double d);
+    public static double tanh(double x) {
+        double t, z;
+        int jx, ix;
+
+        final long bits = Double.doubleToRawLongBits(x);
+        /* High word of |x|. */
+        jx = (int) (bits >>> 32);
+        ix = jx & 0x7fffffff;
+
+        /* x is INF or NaN */
+        if (ix >= 0x7ff00000) {
+            if (jx >= 0) {
+                return 1.00000000000000000000e+00 / x + 1.00000000000000000000e+00; /* ieee_tanh(+-inf)=+-1 */
+            } else {
+                return 1.00000000000000000000e+00 / x - 1.00000000000000000000e+00; /* ieee_tanh(NaN) = NaN */
+            }
+        }
+
+        /* |x| < 22 */
+        if (ix < 0x40360000) { /* |x|<22 */
+            if (ix < 0x3c800000) { /* |x|<2**-55 */
+                return x * (1.00000000000000000000e+00 + x);/* ieee_tanh(small) = small */
+            }
+
+            if (ix >= 0x3ff00000) { /* |x|>=1 */
+                t = Math.expm1(2.0 * Math.abs(x));
+                z = 1.00000000000000000000e+00 - 2.0 / (t + 2.0);
+            } else {
+                t = Math.expm1(-2.0 * Math.abs(x));
+                z = -t / (t + 2.0);
+            }
+            /* |x| > 22, return +-1 */
+        } else {
+            z = 1.00000000000000000000e+00 - TINY; /* raised inexact flag */
+        }
+        return (jx >= 0) ? z : -z;
+    }
 
     /**
      * Returns the measure in degrees of the supplied radian angle. The result
@@ -922,6 +1852,7 @@
     /**
      * Returns a double with the given magnitude and the sign of {@code sign}.
      * If {@code sign} is NaN, the sign of the result is positive.
+     *
      * @since 1.6
      */
     public static double copySign(double magnitude, double sign) {
@@ -932,13 +1863,15 @@
         // (Tested on a Nexus One.)
         long magnitudeBits = Double.doubleToRawLongBits(magnitude);
         long signBits = Double.doubleToRawLongBits((sign != sign) ? 1.0 : sign);
-        magnitudeBits = (magnitudeBits & ~Double.SIGN_MASK) | (signBits & Double.SIGN_MASK);
+        magnitudeBits = (magnitudeBits & ~Double.SIGN_MASK)
+                | (signBits & Double.SIGN_MASK);
         return Double.longBitsToDouble(magnitudeBits);
     }
 
     /**
-     * Returns a float with the given magnitude and the sign of {@code sign}.
-     * If {@code sign} is NaN, the sign of the result is positive.
+     * Returns a float with the given magnitude and the sign of {@code sign}. If
+     * {@code sign} is NaN, the sign of the result is positive.
+     *
      * @since 1.6
      */
     public static float copySign(float magnitude, float sign) {
@@ -949,12 +1882,14 @@
         // (Tested on a Nexus One.)
         int magnitudeBits = Float.floatToRawIntBits(magnitude);
         int signBits = Float.floatToRawIntBits((sign != sign) ? 1.0f : sign);
-        magnitudeBits = (magnitudeBits & ~Float.SIGN_MASK) | (signBits & Float.SIGN_MASK);
+        magnitudeBits = (magnitudeBits & ~Float.SIGN_MASK)
+                | (signBits & Float.SIGN_MASK);
         return Float.intBitsToFloat(magnitudeBits);
     }
 
     /**
      * Returns the exponent of float {@code f}.
+     *
      * @since 1.6
      */
     public static int getExponent(float f) {
@@ -963,14 +1898,17 @@
 
     /**
      * Returns the exponent of double {@code d}.
+     *
      * @since 1.6
      */
-    public static int getExponent(double d){
+    public static int getExponent(double d) {
         return Math.getExponent(d);
     }
 
     /**
-     * Returns the next double after {@code start} in the given {@code direction}.
+     * Returns the next double after {@code start} in the given
+     * {@code direction}.
+     *
      * @since 1.6
      */
     public static double nextAfter(double start, double direction) {
@@ -981,7 +1919,9 @@
     }
 
     /**
-     * Returns the next float after {@code start} in the given {@code direction}.
+     * Returns the next float after {@code start} in the given {@code direction}
+     * .
+     *
      * @since 1.6
      */
     public static float nextAfter(float start, double direction) {
@@ -990,6 +1930,7 @@
 
     /**
      * Returns the next double larger than {@code d}.
+     *
      * @since 1.6
      */
     public static double nextUp(double d) {
@@ -998,6 +1939,7 @@
 
     /**
      * Returns the next float larger than {@code f}.
+     *
      * @since 1.6
      */
     public static float nextUp(float f) {
@@ -1006,6 +1948,7 @@
 
     /**
      * Returns {@code d} * 2^{@code scaleFactor}. The result may be rounded.
+     *
      * @since 1.6
      */
     public static double scalb(double d, int scaleFactor) {
@@ -1049,12 +1992,10 @@
         } else {
             if (Math.abs(d) >= Double.MIN_NORMAL) {
                 // common situation
-                result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS)
-                        | (bits & Double.MANTISSA_MASK);
+                result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS) | (bits & Double.MANTISSA_MASK);
             } else {
                 // origin d is sub-normal, change mantissa to normal style
-                result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS)
-                        | ((bits << (subNormalFactor + 1)) & Double.MANTISSA_MASK);
+                result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS) | ((bits << (subNormalFactor + 1)) & Double.MANTISSA_MASK);
             }
         }
         return Double.longBitsToDouble(result | sign);
@@ -1062,6 +2003,7 @@
 
     /**
      * Returns {@code d} * 2^{@code scaleFactor}. The result may be rounded.
+     *
      * @since 1.6
      */
     public static float scalb(float d, int scaleFactor) {
@@ -1073,8 +2015,7 @@
         int factor = ((bits & Float.EXPONENT_MASK) >> Float.MANTISSA_BITS)
                 - Float.EXPONENT_BIAS + scaleFactor;
         // calculates the factor of sub-normal values
-        int subNormalFactor = Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK)
-                - Float.EXPONENT_BITS;
+        int subNormalFactor = Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK) - Float.EXPONENT_BITS;
         if (subNormalFactor < 0) {
             // not sub-normal values
             subNormalFactor = 0;
@@ -1105,8 +2046,9 @@
                         | (bits & Float.MANTISSA_MASK);
             } else {
                 // origin d is sub-normal, change mantissa to normal style
-                result = ((factor + Float.EXPONENT_BIAS) << Float.MANTISSA_BITS)
-                        | ((bits << (subNormalFactor + 1)) & Float.MANTISSA_MASK);
+                result = ((factor + Float.EXPONENT_BIAS)
+                        << Float.MANTISSA_BITS) | (
+                        (bits << (subNormalFactor + 1)) & Float.MANTISSA_MASK);
             }
         }
         return Float.intBitsToFloat(result | sign);
@@ -1120,10 +2062,10 @@
         }
         // change it to positive
         int absDigits = -digits;
-        if (Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK) <= (32 - absDigits)) {
+        if (Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK)
+                <= (32 - absDigits)) {
             // some bits will remain after shifting, calculates its carry
-            if ((((bits >> (absDigits - 1)) & 0x1) == 0)
-                    || Integer.numberOfTrailingZeros(bits) == (absDigits - 1)) {
+            if ((((bits >> (absDigits - 1)) & 0x1) == 0) || Integer.numberOfTrailingZeros(bits) == (absDigits - 1)) {
                 return bits >> absDigits;
             }
             return ((bits >> absDigits) + 1);
@@ -1139,10 +2081,10 @@
         }
         // change it to positive
         long absDigits = -digits;
-        if (Long.numberOfLeadingZeros(bits & ~Double.SIGN_MASK) <= (64 - absDigits)) {
+        if (Long.numberOfLeadingZeros(bits & ~Double.SIGN_MASK)
+                <= (64 - absDigits)) {
             // some bits will remain after shifting, calculates its carry
-            if ((((bits >> (absDigits - 1)) & 0x1) == 0)
-                    || Long.numberOfTrailingZeros(bits) == (absDigits - 1)) {
+            if ((((bits >> (absDigits - 1)) & 0x1) == 0) || Long.numberOfTrailingZeros(bits) == (absDigits - 1)) {
                 return bits >> absDigits;
             }
             return ((bits >> absDigits) + 1);
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index 9425894..e79f844 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -50,6 +50,7 @@
 import java.util.AbstractMap;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -108,6 +109,33 @@
         unchangeableSystemProperties = initUnchangeableSystemProperties();
         systemProperties = createSystemProperties();
         lineSeparator = System.getProperty("line.separator");
+
+        addLegacyLocaleSystemProperties();
+    }
+
+    private static void addLegacyLocaleSystemProperties() {
+        final String locale = getProperty("user.locale", "");
+        if (!locale.isEmpty()) {
+            Locale l = Locale.forLanguageTag(locale);
+            setUnchangeableSystemProperty("user.language", l.getLanguage());
+            setUnchangeableSystemProperty("user.region", l.getCountry());
+            setUnchangeableSystemProperty("user.variant", l.getVariant());
+        } else {
+            // If "user.locale" isn't set we fall back to our old defaults of
+            // language="en" and region="US" (if unset) and don't attempt to set it.
+            // The Locale class will fall back to using user.language and
+            // user.region if unset.
+            final String language = getProperty("user.language", "");
+            final String region = getProperty("user.region", "");
+
+            if (language.isEmpty()) {
+                setUnchangeableSystemProperty("user.language", "en");
+            }
+
+            if (region.isEmpty()) {
+                setUnchangeableSystemProperty("user.region", "US");
+            }
+        }
     }
 
     /**
@@ -751,8 +779,6 @@
         p.put("java.vm.vendor.url", projectUrl);
 
         p.put("file.encoding", "UTF-8");
-        p.put("user.language", "en");
-        p.put("user.region", "US");
 
         try {
             StructPasswd passwd = Libcore.os.getpwuid(Libcore.os.getuid());
@@ -771,6 +797,13 @@
         p.put("android.icu.unicode.version", ICU.getUnicodeVersion());
         p.put("android.icu.cldr.version", ICU.getCldrVersion());
 
+        // Property override for ICU4J : this is the location of the ICU4C data. This
+        // is prioritized over the properties in ICUConfig.properties. The issue with using
+        // that is that it doesn't play well with jarjar and it needs complicated build rules
+        // to change its default value.
+        String icuDataPath = generateIcuDataPath();
+        p.put("android.icu.impl.ICUBinary.dataPath", icuDataPath);
+
         parsePropertyAssignments(p, specialProperties());
 
         // Override built-in properties with settings from the command line.
@@ -780,9 +813,13 @@
 
     /**
      * Inits an unchangeable system property with the given value.
-     * This is useful when the environment needs to change under native bridge emulation.
+     *
+     * This is called from native code when the environment needs to change under native
+     * bridge emulation.
+     *
+     * @hide also visible for tests.
      */
-    private static void initUnchangeableSystemProperty(String name, String value) {
+    public static void setUnchangeableSystemProperty(String name, String value) {
         checkPropertyName(name);
         unchangeableSystemProperties.put(name, value);
     }
@@ -807,6 +844,37 @@
         return p;
     }
 
+    private static String generateIcuDataPath() {
+        StringBuilder icuDataPathBuilder = new StringBuilder();
+        // ICU should first look in ANDROID_DATA. This is used for (optional) timezone data.
+        String dataIcuDataPath = getEnvironmentPath("ANDROID_DATA", "/misc/zoneinfo/current/icu");
+        if (dataIcuDataPath != null) {
+            icuDataPathBuilder.append(dataIcuDataPath);
+        }
+
+        // ICU should always look in ANDROID_ROOT.
+        String systemIcuDataPath = getEnvironmentPath("ANDROID_ROOT", "/usr/icu");
+        if (systemIcuDataPath != null) {
+            if (icuDataPathBuilder.length() > 0) {
+                icuDataPathBuilder.append(":");
+            }
+            icuDataPathBuilder.append(systemIcuDataPath);
+        }
+        return icuDataPathBuilder.toString();
+    }
+
+    /**
+     * Creates a path by combining the value of an environment variable with a relative path.
+     * Returns {@code null} if the environment variable is not set.
+     */
+    private static String getEnvironmentPath(String environmentVariable, String path) {
+        String variable = getenv(environmentVariable);
+        if (variable == null) {
+            return null;
+        }
+        return variable + path;
+    }
+
     /**
      * Returns an array of "key=value" strings containing information not otherwise
      * easily available, such as #defined library versions.
@@ -964,10 +1032,12 @@
     public static native int identityHashCode(Object anObject);
 
     /**
-     * Returns the system's line separator. On Android, this is {@code "\n"}. The value
-     * comes from the value of the {@code line.separator} system property when the VM
-     * starts. Later changes to the property will not affect the value returned by this
-     * method.
+     * Returns the system's line separator. On Android, this is {@code "\n"}. The value comes from
+     * the value of the {@code line.separator} system property.
+     *
+     * <p>On Android versions before Lollipop the {@code line.separator} system property can be
+     * modified but this method continues to return the original value. The system property cannot
+     * be modified on later versions of Android.
      * @since 1.7
      */
     public static String lineSeparator() {
@@ -1106,7 +1176,12 @@
      * named by the argument. On Android, this would turn {@code "MyLibrary"} into
      * {@code "libMyLibrary.so"}.
      */
-    public static native String mapLibraryName(String nickname);
+    public static String mapLibraryName(String nickname) {
+        if (nickname == null) {
+            throw new NullPointerException("nickname == null");
+        }
+        return "lib" + nickname + ".so";
+    }
 
     /**
      * Used to set System.err, System.in, and System.out.
diff --git a/luni/src/main/java/java/lang/reflect/Modifier.java b/luni/src/main/java/java/lang/reflect/Modifier.java
index 257064e..0480b8b 100644
--- a/luni/src/main/java/java/lang/reflect/Modifier.java
+++ b/luni/src/main/java/java/lang/reflect/Modifier.java
@@ -302,4 +302,23 @@
         buf.setLength(buf.length() - 1);
         return buf.toString();
     }
+
+    /**
+     * Returns the modifiers for fields that can be present in a declaration.
+     * @hide
+     */
+    static String getDeclarationFieldModifiers(int modifiers) {
+        return Modifier.toString(modifiers & fieldModifiers());
+    }
+
+    /**
+     * Returns the modifiers for methods that can be present in a declaration.
+     * @hide
+     */
+    static String getDeclarationMethodModifiers(int modifiers) {
+        return Modifier.toString(modifiers & (
+                Modifier.isConstructor(modifiers)
+                        ? Modifier.constructorModifiers()
+                        : Modifier.methodModifiers()));
+    }
 }
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index f9b72d8..3195240 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -56,7 +56,7 @@
 
     /**
      * Constructs a UDP datagram socket which is bound to any available port on
-     * the localhost.
+     * the local host using a wildcard address.
      *
      * @throws SocketException
      *             if an error occurs while creating or binding the socket.
@@ -67,34 +67,34 @@
 
     /**
      * Constructs a UDP datagram socket which is bound to the specific port
-     * {@code aPort} on the localhost. Valid values for {@code aPort} are
+     * {@code aPort} on the local host using a wildcard address. Valid values for {@code aPort} are
      * between 0 and 65535 inclusive.
      *
      * @param aPort
-     *            the port to bind on the localhost.
+     *            the port to bind on the local host.
      * @throws SocketException
      *             if an error occurs while creating or binding the socket.
      */
     public DatagramSocket(int aPort) throws SocketException {
         checkPort(aPort);
-        createSocket(aPort, Inet4Address.ANY);
+        createSocket(aPort, Inet6Address.ANY);
     }
 
     /**
-     * Constructs a UDP datagram socket which is bound to the specific local
-     * address {@code addr} on port {@code aPort}. Valid values for {@code
-     * aPort} are between 0 and 65535 inclusive.
+     * Constructs a UDP datagram socket which is bound to the specific local address {@code addr} on
+     * port {@code aPort}. Valid values for {@code aPort} are between 0 and 65535 inclusive. If
+     * {@code addr} is {@code null} the socket will be bound to a wildcard address.
      *
      * @param aPort
-     *            the port to bind on the localhost.
+     *            the port to bind on the local host.
      * @param addr
-     *            the address to bind on the localhost.
+     *            the address to bind on the local host.
      * @throws SocketException
      *             if an error occurs while creating or binding the socket.
      */
     public DatagramSocket(int aPort, InetAddress addr) throws SocketException {
         checkPort(aPort);
-        createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr);
+        createSocket(aPort, (addr == null) ? Inet6Address.ANY : addr);
     }
 
     private void checkPort(int aPort) {
@@ -443,7 +443,7 @@
 
     private void ensureBound() throws SocketException {
         if (!isBound()) {
-            impl.bind(0, Inet4Address.ANY);
+            impl.bind(0, Inet6Address.ANY);
             isBound = true;
         }
     }
@@ -467,7 +467,7 @@
         InetAddress addr;
         if (localAddr == null) {
             localPort = 0;
-            addr = Inet4Address.ANY;
+            addr = Inet6Address.ANY;
         } else {
             if (!(localAddr instanceof InetSocketAddress)) {
                 throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
diff --git a/luni/src/main/java/java/net/DatagramSocketImpl.java b/luni/src/main/java/java/net/DatagramSocketImpl.java
index 1a39987..c5e7d70 100644
--- a/luni/src/main/java/java/net/DatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/DatagramSocketImpl.java
@@ -43,13 +43,13 @@
     }
 
     /**
-     * Binds the datagram socket to the given localhost/port. Sockets must be
+     * Binds the datagram socket to the given local host/port. Sockets must be
      * bound prior to attempting to send or receive data.
      *
      * @param port
-     *            the port on the localhost to bind.
+     *            the port on the local host to bind to.
      * @param addr
-     *            the address on the multihomed localhost to bind.
+     *            the address on the multihomed local host to bind to.
      * @throws SocketException
      *                if an error occurs while binding, for example, if the port
      *                has been already bound.
diff --git a/luni/src/main/java/java/net/Inet6Address.java b/luni/src/main/java/java/net/Inet6Address.java
index 8ab0f8d..347f43a 100644
--- a/luni/src/main/java/java/net/Inet6Address.java
+++ b/luni/src/main/java/java/net/Inet6Address.java
@@ -43,7 +43,7 @@
      */
     public static final InetAddress LOOPBACK =
             new Inet6Address(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
-                    "localhost", 0);
+                    "ip6-localhost", 0);
 
     private boolean scope_id_set;
     private int scope_id;
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 5cfa15a..581e1bd 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -202,10 +202,10 @@
     /**
      * Gets all IP addresses associated with the given {@code host} identified
      * by name or literal IP address. The IP address is resolved by the
-     * configured name service. If the host name is empty or {@code null} an
-     * {@code UnknownHostException} is thrown. If the host name is a literal IP
-     * address string an array with the corresponding single {@code InetAddress}
-     * is returned.
+     * configured name service. If the host name is empty or {@code null} the
+     * IP addresses of the loopback interfaces are returned. If the host name
+     * is a literal IP address string an array with the corresponding single
+     * {@code InetAddress} is returned.
      *
      * @param host the hostname or literal IP string to be resolved.
      * @return the array of addresses associated with the specified host.
@@ -344,6 +344,19 @@
     }
 
     /**
+     * Returns the hostname if known, or the result of {@code #getHostAddress}.
+     * Unlike {@link #getHostName}, this method will never cause a DNS lookup.
+     *
+     * @hide For libcore situations that must avoid DNS lookups.
+     */
+    public String getHostString() {
+        if (hostName == null) {
+            return getHostAddress();
+        }
+        return hostName;
+    }
+
+    /**
      * Returns the fully qualified hostname corresponding to this IP address.
      */
     public String getCanonicalHostName() {
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index a366133..1b3eabe 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -21,7 +21,7 @@
 import java.io.ObjectInputStream;
 
 /**
- * This class represents a socket endpoint described by a IP address and a port
+ * This class represents the address of a socket endpoint described by a IP address and a port
  * number. It is a concrete implementation of {@code SocketAddress} for IP.
  */
 public class InetSocketAddress extends SocketAddress {
@@ -48,8 +48,7 @@
      * no specified address. The range for valid port numbers is between 0 and
      * 65535 inclusive.
      *
-     * @param port
-     *            the specified port number to which this socket is bound.
+     * @param port the port number of the socket endpoint.
      */
     public InetSocketAddress(int port) {
         this((InetAddress) null, port);
@@ -58,19 +57,17 @@
     /**
      * Creates a socket endpoint with the given port number {@code port} and
      * {@code address}. The range for valid port numbers is between 0 and 65535
-     * inclusive. If {@code address} is {@code null} this socket is bound to the
-     * IPv4 wildcard address.
+     * inclusive. If {@code address} is {@code null} the address is set to a
+     * wildcard address.
      *
-     * @param port
-     *            the specified port number to which this socket is bound.
-     * @param address
-     *            the specified address to which this socket is bound.
+     * @param address the address of the socket endpoint.
+     * @param port the port number of the socket endpoint.
      */
     public InetSocketAddress(InetAddress address, int port) {
         if (port < 0 || port > 65535) {
             throw new IllegalArgumentException("port=" + port);
         }
-        this.addr = (address == null) ? Inet4Address.ANY : address;
+        this.addr = (address == null) ? Inet6Address.ANY : address;
         this.hostname = null;
         this.port = port;
     }
@@ -81,10 +78,8 @@
      * {@code null}. The range for valid port numbers is between 0 and 65535
      * inclusive.
      *
-     * @param port
-     *            the specified port number to which this socket is bound.
-     * @param host
-     *            the specified hostname to which this socket is bound.
+     * @param host the hostname of the socket endpoint.
+     * @param port the port number of the socket endpoint.
      */
     public InetSocketAddress(String host, int port) {
         this(host, port, true);
@@ -117,28 +112,25 @@
      * hostname into an {@code InetAddress}. The address field is marked as
      * unresolved.
      *
-     * @param host
-     *            the specified hostname to which this socket is bound.
-     * @param port
-     *            the specified port number to which this socket is bound.
+     * @param host the hostname of the socket endpoint.
+     * @param port the port number of the socket endpoint.
      * @return the created InetSocketAddress instance.
-     * @throws IllegalArgumentException
-     *             if the hostname {@code host} is {@code null} or the port is
-     *             not in the range between 0 and 65535.
+     * @throws IllegalArgumentException if the hostname {@code host} is {@code null} or the port is
+     *         not in the range between 0 and 65535.
      */
     public static InetSocketAddress createUnresolved(String host, int port) {
         return new InetSocketAddress(host, port, false);
     }
 
     /**
-     * Returns this socket address' port.
+     * Returns the socket endpoint's port.
      */
     public final int getPort() {
         return port;
     }
 
     /**
-     * Returns this socket address' address.
+     * Returns the socket endpoint's address.
      */
     public final InetAddress getAddress() {
         return addr;
@@ -159,7 +151,7 @@
      * @since 1.7
      */
     public final String getHostString() {
-        return (hostname != null) ? hostname : addr.getHostAddress();
+        return (hostname != null) ? hostname : addr.getHostString();
     }
 
     /**
@@ -186,9 +178,8 @@
      * socket endpoints are equal if the IP address or the hostname of both are
      * equal and they are bound to the same port.
      *
-     * @param socketAddr
-     *            the object to be tested for equality.
-     * @return {@code true} if this socket and the given socket object {@code
+     * @param socketAddr the object to be tested for equality.
+     * @return {@code true} if this socket endpoint and the given socket endpoint {@code
      *         socketAddr} are equal, {@code false} otherwise.
      */
     @Override
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index 4e5ba44..0c50f62 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -301,16 +301,12 @@
      * Gets the InetAddress of the SOCKS proxy server.
      */
     private InetAddress socksGetServerAddress() throws UnknownHostException {
-        String proxyName;
         // get socks server address from proxy. It is unnecessary to check
         // "socksProxyHost" property, since all proxy setting should be
         // determined by ProxySelector.
-        InetSocketAddress addr = (InetSocketAddress) proxy.address();
-        proxyName = addr.getHostName();
-        if (proxyName == null) {
-            proxyName = addr.getAddress().getHostAddress();
-        }
-        return InetAddress.getByName(proxyName);
+        InetSocketAddress socketAddress = (InetSocketAddress) proxy.address();
+        InetAddress address = socketAddress.getAddress();
+        return (address != null) ? address : InetAddress.getByName(socketAddress.getHostName());
     }
 
     /**
diff --git a/luni/src/main/java/java/net/ServerSocket.java b/luni/src/main/java/java/net/ServerSocket.java
index 72b197f..a2cd9c6 100644
--- a/luni/src/main/java/java/net/ServerSocket.java
+++ b/luni/src/main/java/java/net/ServerSocket.java
@@ -65,24 +65,25 @@
     }
 
     /**
-     * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
-     * The backlog is set to 50. If {@code port == 0}, a port will be assigned by the OS.
+     * Constructs a new {@code ServerSocket} instance bound to the given {@code port} using a
+     * wildcard address. The backlog is set to 50. If {@code port == 0}, a port will be assigned by
+     * the OS.
      *
      * @throws IOException if an error occurs while creating the socket.
      */
     public ServerSocket(int port) throws IOException {
-        this(port, DEFAULT_BACKLOG, Inet4Address.ANY);
+        this(port, DEFAULT_BACKLOG, Inet6Address.ANY);
     }
 
     /**
-     * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
-     * The backlog is set to {@code backlog}.
+     * Constructs a new {@code ServerSocket} instance bound to the given {@code port} using a
+     * wildcard address. The backlog is set to {@code backlog}.
      * If {@code port == 0}, a port will be assigned by the OS.
      *
      * @throws IOException if an error occurs while creating the socket.
      */
     public ServerSocket(int port, int backlog) throws IOException {
-        this(port, backlog, Inet4Address.ANY);
+        this(port, backlog, Inet6Address.ANY);
     }
 
     /**
@@ -97,7 +98,7 @@
         checkListen(port);
         this.impl = factory != null ? factory.createSocketImpl()
                 : new PlainServerSocketImpl();
-        InetAddress addr = (localAddress == null) ? Inet4Address.ANY : localAddress;
+        InetAddress addr = (localAddress == null) ? Inet6Address.ANY : localAddress;
 
         synchronized (this) {
             impl.create(true);
@@ -316,7 +317,7 @@
         InetAddress addr;
         int port;
         if (localAddr == null) {
-            addr = Inet4Address.ANY;
+            addr = Inet6Address.ANY;
             port = 0;
         } else {
             if (!(localAddr instanceof InetSocketAddress)) {
diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java
index 5dd350a..7ee6b05 100644
--- a/luni/src/main/java/java/net/Socket.java
+++ b/luni/src/main/java/java/net/Socket.java
@@ -41,7 +41,7 @@
     private boolean isInputShutdown = false;
     private boolean isOutputShutdown = false;
 
-    private InetAddress localAddress = Inet4Address.ANY;
+    private InetAddress localAddress = Inet6Address.ANY;
 
     private final Object connectLock = new Object();
 
@@ -211,7 +211,7 @@
     /**
      * Creates a new streaming socket connected to the target host specified by
      * the parameters {@code dstAddress} and {@code dstPort}. The socket is
-     * bound to any available port on the local host.
+     * bound to any available port on the local host using a wildcard address.
      *
      * @param dstAddress
      *            the target host address to connect to.
@@ -228,16 +228,18 @@
 
     /**
      * Creates a new streaming socket connected to the target host specified by
-     * the parameters {@code dstAddress} and {@code dstPort}. On the local
-     * endpoint the socket is bound to the given address {@code localAddress} on
-     * port {@code localPort}.
+     * the parameters {@code dstAddress} and {@code dstPort}.
+     *
+     * <p>On the local endpoint the socket is bound to the given address {@code localAddress} on
+     * port {@code localPort}. If {@code localAddress} is {@code null} the socket will be bound to a
+     * wildcard address.
      *
      * @param dstAddress
      *            the target host address to connect to.
      * @param dstPort
      *            the port on the target host to connect to.
      * @param localAddress
-     *            the address on the local host to bind to.
+     *            the address on the local host to bind to, or null.
      * @param localPort
      *            the port on the local host to bind to.
      * @throws IOException
@@ -253,7 +255,7 @@
     /**
      * Creates a new streaming or datagram socket connected to the target host
      * specified by the parameters {@code addr} and {@code port}. The socket is
-     * bound to any available port on the local host.
+     * bound to any available port on the local host using a wildcard address.
      *
      * @param addr
      *            the Internet address to connect to.
@@ -315,7 +317,7 @@
         isConnected = false;
         // RI compatibility: the RI returns the any address (but the original local port) after
         // close.
-        localAddress = Inet4Address.ANY;
+        localAddress = Inet6Address.ANY;
         impl.close();
     }
 
@@ -330,7 +332,7 @@
         isConnected = false;
         // RI compatibility: the RI returns the any address (but the original local port) after
         // close.
-        localAddress = Inet4Address.ANY;
+        localAddress = Inet6Address.ANY;
         impl.onClose();
     }
 
@@ -577,7 +579,7 @@
             throw new IllegalArgumentException("Local port out of range: " + localPort);
         }
 
-        InetAddress addr = localAddress == null ? Inet4Address.ANY : localAddress;
+        InetAddress addr = localAddress == null ? Inet6Address.ANY : localAddress;
         synchronized (this) {
             impl.create(streaming);
             isCreated = true;
@@ -772,7 +774,7 @@
         InetAddress addr;
         if (localAddr == null) {
             port = 0;
-            addr = Inet4Address.ANY;
+            addr = Inet6Address.ANY;
         } else {
             if (!(localAddr instanceof InetSocketAddress)) {
                 throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
@@ -875,7 +877,7 @@
                     // options on create
                     // impl.create(true);
                     if (!usingSocks()) {
-                        impl.bind(Inet4Address.ANY, 0);
+                        impl.bind(Inet6Address.ANY, 0);
                     }
                     isBound = true;
                 }
diff --git a/luni/src/main/java/java/net/URL.java b/luni/src/main/java/java/net/URL.java
index dd487bc..b62ed84 100644
--- a/luni/src/main/java/java/net/URL.java
+++ b/luni/src/main/java/java/net/URL.java
@@ -331,7 +331,7 @@
      * Virtual hosting permits unrelated sites to share an IP address. This
      * method could report two otherwise unrelated URLs to be equal because
      * they're hosted on the same server.</li>
-     * <li><strong>The network many not be available.</strong> Two URLs could be
+     * <li><strong>The network may not be available.</strong> Two URLs could be
      * equal when a network is available and unequal otherwise.</li>
      * <li><strong>The network may change.</strong> The IP address for a given
      * host name varies by network and over time. This is problematic for mobile
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index 45406b1..c73ba22 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -38,7 +38,6 @@
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 
-import static android.system.OsConstants.EINTR;
 import static android.system.OsConstants.POLLERR;
 import static android.system.OsConstants.POLLHUP;
 import static android.system.OsConstants.POLLIN;
@@ -94,7 +93,7 @@
          * configure the pipe so we can fully drain it without blocking.
          */
         try {
-            FileDescriptor[] pipeFds = Libcore.os.pipe();
+            FileDescriptor[] pipeFds = Libcore.os.pipe2(0);
             wakeupIn = pipeFds[0];
             wakeupOut = pipeFds[1];
             IoUtils.setBlocking(wakeupIn, false);
@@ -183,9 +182,7 @@
                         try {
                             rc = Libcore.os.poll(pollFds.array(), (int) timeout);
                         } catch (ErrnoException errnoException) {
-                            if (errnoException.errno != EINTR) {
-                                throw errnoException.rethrowAsIOException();
-                            }
+                            throw errnoException.rethrowAsIOException();
                         }
                     } finally {
                         if (isBlocking) {
diff --git a/luni/src/main/java/java/nio/channels/Selector.java b/luni/src/main/java/java/nio/channels/Selector.java
index 6d9b063..baa6a7f 100644
--- a/luni/src/main/java/java/nio/channels/Selector.java
+++ b/luni/src/main/java/java/nio/channels/Selector.java
@@ -78,8 +78,11 @@
     public abstract boolean isOpen();
 
     /**
-     * Gets the set of registered keys. The set is immutable and is not thread-
-     * safe.
+     * Gets the set of registered keys.
+     *
+     * <p>The returned set cannot be changed directly but can be modified
+     * indirectly by operations on the Selector. It should therefore not be
+     * treated as thread-safe.
      *
      * @return the set of registered keys.
      */
@@ -127,9 +130,11 @@
     public abstract int select(long timeout) throws IOException;
 
     /**
-     * Gets the selection keys whose channels are ready for operation. The set
-     * is not thread-safe and no keys may be added to it. Removing keys is
-     * allowed.
+     * Gets the selection keys whose channels are ready for operation.
+     *
+     * <p>Keys cannot be added to the set directly. Keys can be removed.
+     * The set can be modified indirectly by operations on the Selector. It
+     * should therefore not be treated as thread-safe.
      *
      * @return the selection keys whose channels are ready for operation.
      * @throws ClosedSelectorException
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
index 9217bba..9d53328 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
@@ -194,13 +194,19 @@
             throw illegalStateException();
         }
 
+        if (!cb.hasRemaining()) {
+            return true;
+        }
+
         CodingErrorAction originalMalformedInputAction = malformedInputAction;
         CodingErrorAction originalUnmappableCharacterAction = unmappableCharacterAction;
         onMalformedInput(CodingErrorAction.REPORT);
         onUnmappableCharacter(CodingErrorAction.REPORT);
         try {
-            encode(cb);
-            return true;
+            ByteBuffer buf = encode(cb);
+            // b/18474439: ICU will return U_ZERO_ERROR but produce an output buffer
+            // of size zero when it encounters an ignorable codepoint.
+            return buf.hasRemaining();
         } catch (CharacterCodingException e) {
             return false;
         } finally {
@@ -436,24 +442,33 @@
      * <p>
      * This method will call {@link #implFlush(ByteBuffer) implFlush}. Some
      * encoders may need to write some bytes to the output buffer when they have
-     * read all input characters, subclasses can overridden
-     * {@link #implFlush(ByteBuffer) implFlush} to perform writing action.
+     * read all input characters. Subclasses can override
+     * {@link #implFlush(ByteBuffer) implFlush} to perform any writes that are
+     * required at the end of the output sequence, such as footers and other
+     * metadata.
      * <p>
-     * The maximum number of written bytes won't larger than
-     * {@link ByteBuffer#remaining() out.remaining()}. If some encoder wants to
+     * The maximum number of written bytes won't be larger than
+     * {@link ByteBuffer#remaining() out.remaining()}. If the encoder wants to
      * write more bytes than the output buffer's available remaining space, then
-     * <code>CoderResult.OVERFLOW</code> will be returned, and this method
-     * must be called again with a byte buffer that has free space. Otherwise
-     * this method will return <code>CoderResult.UNDERFLOW</code>, which
-     * means one encoding process has been completed successfully.
+     * it will return {@code CoderResult.OVERFLOW}. This method must then be
+     * called again with a byte buffer that has free space.
+     * <p>
+     * If the encoder was asked to flush its output when its input is incomplete,
+     * (because it ends with an unpaired surrogate, say) it may return
+     * {@code CodeResult.MALFORMED}.
+     * <p>
+     * In all other cases the encoder will return {@code CoderResult.UNDERFLOW},
+     * which signifies that all the input so far has been successfully encoded.
      * <p>
      * During the flush, the output buffer's position will be changed
      * accordingly, while its mark and limit will be intact.
+     * <p>
+     * This method is a no-op if the encoder has already been flushed.
      *
-     * @param out
-     *            the given output buffer.
-     * @return <code>CoderResult.UNDERFLOW</code> or
-     *         <code>CoderResult.OVERFLOW</code>.
+     * @param out the given output buffer.
+     * @return {@code CoderResult.UNDERFLOW} or
+     *         {@code CoderResult.OVERFLOW} or
+     *         {@code CoderResult.MALFORMED}
      * @throws IllegalStateException
      *             if this encoder isn't already flushed or at end of input.
      */
@@ -461,6 +476,9 @@
         if (state != FLUSHED && state != END_OF_INPUT) {
             throw illegalStateException();
         }
+        if (state == FLUSHED) {
+            return CoderResult.UNDERFLOW;
+        }
         CoderResult result = implFlush(out);
         if (result == CoderResult.UNDERFLOW) {
             state = FLUSHED;
diff --git a/luni/src/main/java/java/security/Security.java b/luni/src/main/java/java/security/Security.java
index b859f9a..aeb189f 100644
--- a/luni/src/main/java/java/security/Security.java
+++ b/luni/src/main/java/java/security/Security.java
@@ -19,6 +19,8 @@
 
 import java.io.BufferedInputStream;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -47,16 +49,25 @@
     // - load security properties files
     // - load statically registered providers
     // - if no provider description file found then load default providers
+    // Note: Getting the input stream for the security.properties file is factored into its own
+    //       function, which will be intercepted during boot image creation.
     static {
         boolean loaded = false;
+        Reader input = null;
         try {
-            InputStream configStream = Security.class.getResourceAsStream("security.properties");
-            InputStream input = new BufferedInputStream(configStream);
+            input = getSecurityPropertiesReader();
             secprops.load(input);
             loaded = true;
-            configStream.close();
         } catch (Exception ex) {
             System.logE("Could not load 'security.properties'", ex);
+        } finally {
+            if (input != null) {
+                try {
+                    input.close();
+                } catch (Exception ex) {
+                    System.logW("Could not close 'security.properties'", ex);
+                }
+            }
         }
         if (!loaded) {
             registerDefaultProviders();
@@ -64,6 +75,11 @@
         Engine.door = new SecurityDoor();
     }
 
+    private static Reader getSecurityPropertiesReader() throws Exception {
+        InputStream configStream = Security.class.getResourceAsStream("security.properties");
+        return new InputStreamReader(new BufferedInputStream(configStream), "ISO-8859-1");
+    }
+
     /**
      * This class can't be instantiated.
      */
diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java
index a39d59b..3151058 100644
--- a/luni/src/main/java/java/security/Signature.java
+++ b/luni/src/main/java/java/security/Signature.java
@@ -193,7 +193,7 @@
             if (service == null) {
                 return null;
             }
-            return tryAlgorithmWithProvider(key, service);
+            return tryAlgorithmWithProvider(null, service);
         }
         ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
         if (services == null) {
@@ -245,6 +245,15 @@
     }
 
     /**
+     * Gets the SPI implementation backing this signature.
+     *
+     * @hide
+     */
+    public SignatureSpi getSpi() {
+        return null;
+    }
+
+    /**
      * Returns the name of the algorithm of this {@code Signature}.
      *
      * @return the name of the algorithm of this {@code Signature}.
@@ -730,8 +739,11 @@
 
         /**
          * Convenience call when the Key is not available.
+         *
+         * @hide
          */
-        private SignatureSpi getSpi() {
+        @Override
+        public SignatureSpi getSpi() {
             return getSpi(null);
         }
     }
diff --git a/luni/src/main/java/java/text/BreakIterator.java b/luni/src/main/java/java/text/BreakIterator.java
index 81545b2..051ea15 100644
--- a/luni/src/main/java/java/text/BreakIterator.java
+++ b/luni/src/main/java/java/text/BreakIterator.java
@@ -18,8 +18,6 @@
 package java.text;
 
 import java.util.Locale;
-import libcore.icu.ICU;
-import libcore.icu.NativeBreakIterator;
 
 /**
  * Locates boundaries in text. This class defines a protocol for objects that
@@ -230,29 +228,19 @@
      */
     public static final int DONE = -1;
 
-    // the wrapped ICU implementation
-    NativeBreakIterator wrapped;
-
     /**
      * Default constructor, for use by subclasses.
      */
     protected BreakIterator() {
     }
 
-    /*
-     * wrapping constructor
-     */
-    BreakIterator(NativeBreakIterator iterator) {
-        wrapped = iterator;
-    }
-
     /**
      * Returns an array of locales for which custom {@code BreakIterator} instances
      * are available.
      * <p>Note that Android does not support user-supplied locale service providers.
      */
     public static Locale[] getAvailableLocales() {
-        return ICU.getAvailableBreakIteratorLocales();
+        return com.ibm.icu.text.BreakIterator.getAvailableLocales();
     }
 
     /**
@@ -270,7 +258,8 @@
      * characters using the given locale.
      */
     public static BreakIterator getCharacterInstance(Locale locale) {
-        return new RuleBasedBreakIterator(NativeBreakIterator.getCharacterInstance(locale));
+        return new IcuIteratorWrapper(
+                com.ibm.icu.text.BreakIterator.getCharacterInstance(locale));
     }
 
     /**
@@ -288,7 +277,8 @@
      * line breaks using the given locale.
      */
     public static BreakIterator getLineInstance(Locale locale) {
-        return new RuleBasedBreakIterator(NativeBreakIterator.getLineInstance(locale));
+        return new IcuIteratorWrapper(
+                com.ibm.icu.text.BreakIterator.getLineInstance(locale));
     }
 
     /**
@@ -306,7 +296,8 @@
      * sentence-breaks using the given locale.
      */
     public static BreakIterator getSentenceInstance(Locale locale) {
-        return new RuleBasedBreakIterator(NativeBreakIterator.getSentenceInstance(locale));
+        return new IcuIteratorWrapper(
+                com.ibm.icu.text.BreakIterator.getSentenceInstance(locale));
     }
 
     /**
@@ -324,7 +315,8 @@
      * word-breaks using the given locale.
      */
     public static BreakIterator getWordInstance(Locale locale) {
-        return new RuleBasedBreakIterator(NativeBreakIterator.getWordInstance(locale));
+        return new IcuIteratorWrapper(
+                com.ibm.icu.text.BreakIterator.getWordInstance(locale));
     }
 
     /**
@@ -339,7 +331,7 @@
      *         false} otherwise.
      */
     public boolean isBoundary(int offset) {
-        return wrapped.isBoundary(offset);
+        return false;
     }
 
     /**
@@ -354,7 +346,7 @@
      *            if the offset is invalid.
      */
     public int preceding(int offset) {
-        return wrapped.preceding(offset);
+        return 0;
     }
 
     /**
@@ -366,10 +358,6 @@
      *            the new text string to be analyzed.
      */
     public void setText(String newText) {
-        if (newText == null) {
-            throw new NullPointerException("newText == null");
-        }
-        wrapped.setText(newText);
     }
 
     /**
@@ -466,9 +454,7 @@
     @Override
     public Object clone() {
         try {
-            BreakIterator cloned = (BreakIterator) super.clone();
-            cloned.wrapped = (NativeBreakIterator) wrapped.clone();
-            return cloned;
+            return (BreakIterator) super.clone();
         } catch (CloneNotSupportedException e) {
             throw new AssertionError(e);
         }
diff --git a/luni/src/main/java/java/text/ChoiceFormat.java b/luni/src/main/java/java/text/ChoiceFormat.java
index 014b8c7..3d3cb3a 100644
--- a/luni/src/main/java/java/text/ChoiceFormat.java
+++ b/luni/src/main/java/java/text/ChoiceFormat.java
@@ -303,37 +303,16 @@
     }
 
     /**
-     * Returns the double value which is closest to the specified double but
-     * larger.
-     *
-     * @param value
-     *            a double value.
-     * @return the next larger double value.
+     * Equivalent to {@link Math#nextUp(double)}.
      */
     public static final double nextDouble(double value) {
-        if (value == Double.POSITIVE_INFINITY) {
-            return value;
-        }
-        long bits;
-        // Handle -0.0
-        if (value == 0) {
-            bits = 0;
-        } else {
-            bits = Double.doubleToLongBits(value);
-        }
-        return Double.longBitsToDouble(value < 0 ? bits - 1 : bits + 1);
+        return Math.nextUp(value);
     }
 
     /**
-     * Returns the double value which is closest to the specified double but
-     * either larger or smaller as specified.
-     *
-     * @param value
-     *            a double value.
-     * @param increment
-     *            {@code true} to get the next larger value, {@code false} to
-     *            get the previous smaller value.
-     * @return the next larger or smaller double value.
+     * Equivalent to {@link Math#nextUp(double)} if {@code increment == true}, and
+     * {@link Math#nextAfter(double, double)} with {@code direction == Double.NEGATIVE_INFINITY}
+     * otherwise.
      */
     public static double nextDouble(double value, boolean increment) {
         return increment ? nextDouble(value) : previousDouble(value);
@@ -385,25 +364,11 @@
     }
 
     /**
-     * Returns the double value which is closest to the specified double but
-     * smaller.
-     *
-     * @param value
-     *            a double value.
-     * @return the next smaller double value.
+     * Equivalent to {@link Math#nextAfter(double, double)} with
+     * {@code direction == Double.NEGATIVE_INFINITY}.
      */
     public static final double previousDouble(double value) {
-        if (value == Double.NEGATIVE_INFINITY) {
-            return value;
-        }
-        long bits;
-        // Handle 0.0
-        if (value == 0) {
-            bits = 0x8000000000000000L;
-        } else {
-            bits = Double.doubleToLongBits(value);
-        }
-        return Double.longBitsToDouble(value <= 0 ? bits + 1 : bits - 1);
+        return Math.nextAfter(value, Double.NEGATIVE_INFINITY);
     }
 
     /**
@@ -453,9 +418,30 @@
             if (i != 0) {
                 buffer.append('|');
             }
-            String previous = String.valueOf(previousDouble(choiceLimits[i]));
-            String limit = String.valueOf(choiceLimits[i]);
-            if (previous.length() < limit.length()) {
+
+            final String previous = String.valueOf(previousDouble(choiceLimits[i]));
+            final String limit = String.valueOf(choiceLimits[i]);
+
+            // Hack to make the output of toPattern parseable by another ChoiceFormat.
+            // String.valueOf() will emit "Infinity", which isn't parseable by our NumberFormat
+            // instances.
+            //
+            // Ideally, we'd just use NumberFormat.format() to emit output (to be symmetric with
+            // our usage of NumberFormat.parse()) but it's hard set the right number of significant
+            // digits in order to output a format string that's equivalent to the original input.
+            if (Double.isInfinite(choiceLimits[i]) ||
+                    Double.isInfinite(previousDouble(choiceLimits[i]))) {
+                if (choiceLimits[i] < 0) {
+                    buffer.append("-\u221E");
+                    buffer.append('<');
+                } else {
+                    buffer.append('\u221E');
+                    buffer.append('<');
+                }
+            } else if (previous.length() < limit.length()) {
+                // What the... i don't even.... sigh. This is trying to figure out whether the
+                // element was a "<" or a "#". The idea being that users will specify "reasonable"
+                // quantities and calling nextDouble will result in a "longer" number in most cases.
                 buffer.append(previous);
                 buffer.append('<');
             } else {
diff --git a/luni/src/main/java/java/text/Format.java b/luni/src/main/java/java/text/Format.java
index 58671fa..c4dc5f0 100644
--- a/luni/src/main/java/java/text/Format.java
+++ b/luni/src/main/java/java/text/Format.java
@@ -177,23 +177,26 @@
     static boolean upTo(String string, ParsePosition position,
             StringBuffer buffer, char stop) {
         int index = position.getIndex(), length = string.length();
-        boolean lastQuote = false, quote = false;
+
+        int numConsecutiveQuotes = 0;
+        boolean quote = false;
         while (index < length) {
             char ch = string.charAt(index++);
             if (ch == '\'') {
-                if (lastQuote) {
+                ++numConsecutiveQuotes;
+                if (numConsecutiveQuotes != 0 && numConsecutiveQuotes % 2 == 0) {
                     buffer.append('\'');
                 }
                 quote = !quote;
-                lastQuote = true;
             } else if (ch == stop && !quote) {
                 position.setIndex(index);
                 return true;
             } else {
-                lastQuote = false;
+                numConsecutiveQuotes = 0;
                 buffer.append(ch);
             }
         }
+
         position.setIndex(index);
         return false;
     }
diff --git a/luni/src/main/java/java/text/RuleBasedBreakIterator.java b/luni/src/main/java/java/text/IcuIteratorWrapper.java
similarity index 80%
rename from luni/src/main/java/java/text/RuleBasedBreakIterator.java
rename to luni/src/main/java/java/text/IcuIteratorWrapper.java
index a16968e..b863fa7 100644
--- a/luni/src/main/java/java/text/RuleBasedBreakIterator.java
+++ b/luni/src/main/java/java/text/IcuIteratorWrapper.java
@@ -17,7 +17,6 @@
 
 package java.text;
 
-import libcore.icu.NativeBreakIterator;
 
 /*
  * Default implementation of BreakIterator. Wraps libcore.icu.NativeBreakIterator.
@@ -25,10 +24,13 @@
  * and we don't have Java implementations of those methods (other than the current ones, which
  * forward to the wrapped NativeBreakIterator).
  */
-class RuleBasedBreakIterator extends BreakIterator {
+class IcuIteratorWrapper extends BreakIterator {
 
-    RuleBasedBreakIterator(NativeBreakIterator iterator) {
-        super(iterator);
+    /* The wrapped ICU implementation. Non-final for #clone() */
+    private com.ibm.icu.text.BreakIterator wrapped;
+
+    IcuIteratorWrapper(com.ibm.icu.text.BreakIterator iterator) {
+        wrapped = iterator;
     }
 
     @Override public int current() {
@@ -44,17 +46,6 @@
         return wrapped.following(offset);
     }
 
-    private void checkOffset(int offset) {
-        if (!wrapped.hasText()) {
-            throw new IllegalArgumentException("BreakIterator has no text");
-        }
-        CharacterIterator it = wrapped.getText();
-        if (offset < it.getBeginIndex() || offset > it.getEndIndex()) {
-            String message = "Valid range is [" + it.getBeginIndex() + " " + it.getEndIndex() + "]";
-            throw new IllegalArgumentException(message);
-        }
-    }
-
     @Override public CharacterIterator getText() {
         return wrapped.getText();
     }
@@ -75,10 +66,11 @@
         return wrapped.previous();
     }
 
+    @Override public void setText(String newText) {
+        wrapped.setText(newText);
+    }
+
     @Override public void setText(CharacterIterator newText) {
-        if (newText == null) {
-            throw new NullPointerException("newText == null");
-        }
         newText.current();
         wrapped.setText(newText);
     }
@@ -94,10 +86,10 @@
     }
 
     @Override public boolean equals(Object o) {
-        if (!(o instanceof RuleBasedBreakIterator)) {
+        if (!(o instanceof IcuIteratorWrapper)) {
             return false;
         }
-        return wrapped.equals(((RuleBasedBreakIterator) o).wrapped);
+        return wrapped.equals(((IcuIteratorWrapper) o).wrapped);
     }
 
     @Override public String toString() {
@@ -108,9 +100,20 @@
         return wrapped.hashCode();
     }
 
+    private void checkOffset(int offset) {
+        final CharacterIterator it = wrapped.getText();
+        if (it == null) {
+            throw new IllegalArgumentException("BreakIterator has no text");
+        }
+        if (offset < it.getBeginIndex() || offset > it.getEndIndex()) {
+            String message = "Valid range is [" + it.getBeginIndex() + " " + it.getEndIndex() + "]";
+            throw new IllegalArgumentException(message);
+        }
+    }
+
     @Override public Object clone() {
-        RuleBasedBreakIterator cloned = (RuleBasedBreakIterator) super.clone();
-        cloned.wrapped = (NativeBreakIterator) wrapped.clone();
+        IcuIteratorWrapper cloned = (IcuIteratorWrapper) super.clone();
+        cloned.wrapped = (com.ibm.icu.text.BreakIterator) wrapped.clone();
         return cloned;
     }
 }
diff --git a/luni/src/main/java/java/text/MessageFormat.java b/luni/src/main/java/java/text/MessageFormat.java
index cf306a7..f48cebd 100644
--- a/luni/src/main/java/java/text/MessageFormat.java
+++ b/luni/src/main/java/java/text/MessageFormat.java
@@ -580,6 +580,12 @@
             }
             if (format instanceof ChoiceFormat) {
                 String result = format.format(arg);
+                // Escape quotes in the result because the ChoiceFormat would've already
+                // dealt with them for us. In other words, any quotes that are present in the
+                // result are due to escaped quotes in the original input. We should preserve
+                // them in the output instead of having them processed again with the message
+                // format we're creating below.
+                result = result.replace("'", "''");
                 MessageFormat mf = new MessageFormat(result);
                 mf.setLocale(locale);
                 mf.format(objects, buffer, passedField);
diff --git a/luni/src/main/java/java/text/NumberFormat.java b/luni/src/main/java/java/text/NumberFormat.java
index a1f10d4..2526ffc 100644
--- a/luni/src/main/java/java/text/NumberFormat.java
+++ b/luni/src/main/java/java/text/NumberFormat.java
@@ -492,6 +492,10 @@
      * to format floating-point numbers typically between 0 and 1 (with 1 being 100%).
      * A value such as 0.53 will be treated as 53%, but 53.0 (or the integer 53) will be
      * treated as 5,300%, which is rarely what you intended.
+     *
+     * <p>Non-integer percentages will be rounded according to the rounding mode,
+     * so by default 0.142 will be 14% but 0.148 will be 15%. If you want fractional
+     * percentages, use {@link #setMaximumFractionDigits}.
      */
     public static final NumberFormat getPercentInstance() {
         return getPercentInstance(Locale.getDefault());
@@ -505,6 +509,10 @@
      * to format floating-point numbers typically between 0 and 1 (with 1 being 100%).
      * A value such as 0.53 will be treated as 53%, but 53.0 (or the integer 53) will be
      * treated as 5,300%, which is rarely what you intended.
+     *
+     * <p>Non-integer percentages will be rounded according to the rounding mode,
+     * so by default 0.142 will be 14% but 0.148 will be 15%. If you want fractional
+     * percentages, use {@link #setMaximumFractionDigits}.
      */
     public static NumberFormat getPercentInstance(Locale locale) {
         if (locale == null) {
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 8f83ff7..4e7a950 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -91,7 +91,16 @@
  * <tr> <td>{@code ''}</td> <td>single quote</td>           <td>(Literal)</td>     <td>{@code 'o''clock'}:o'clock</td> </tr>
  * </table>
  *
- * <p>Fractional seconds are handled specially: they're zero-padded on the <i>right</i>.
+ * <p>Note that {@code 'S'} represents fractional seconds and not millisecond values.
+ * They will be padded on the left or on the right or both depending on the number of
+ * {@code 'S'} in the pattern. For example, the number of fractional seconds in a
+ * {@code Date} where {@code Date.getTime() == 1000006} are {@code 0.006} or
+ * {@code (6 / 1000)}. This leads to the following formatting:
+ * <ul>
+ *     <li> {@code "S" => "0"} </li>
+ *     <li> {@code "SSS" => "006" } </li>
+ *     <li> {@code "SSSSSS" => "006000" }</li>
+ * </ul>
  *
  * <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
diff --git a/luni/src/main/java/java/util/Calendar.java b/luni/src/main/java/java/util/Calendar.java
index fc4cef6..6057547 100644
--- a/luni/src/main/java/java/util/Calendar.java
+++ b/luni/src/main/java/java/util/Calendar.java
@@ -810,7 +810,9 @@
     }
 
     /**
-     * Returns a shallow copy of this {@code Calendar} with the same properties.
+     * Returns a partially deep copy of this {@code Calendar}; all fields from
+     * from the {@code Calendar} class are cloned (deep copy) but fields from
+     * subclasses aren't (shallow copy).
      */
     @Override
     public Object clone() {
diff --git a/luni/src/main/java/java/util/Collections.java b/luni/src/main/java/java/util/Collections.java
index 4541d64..dc4161f 100644
--- a/luni/src/main/java/java/util/Collections.java
+++ b/luni/src/main/java/java/util/Collections.java
@@ -102,6 +102,10 @@
             throw new IndexOutOfBoundsException();
         }
 
+        @Override public Iterator iterator() {
+            return EMPTY_ITERATOR;
+        }
+
         private Object readResolve() {
             return Collections.EMPTY_LIST;
         }
@@ -1422,21 +1426,21 @@
         if (!(list instanceof RandomAccess)) {
             ListIterator<? extends Comparable<? super T>> it = list.listIterator();
             while (it.hasNext()) {
-                int result;
-                if ((result = -it.next().compareTo(object)) <= 0) {
-                    if (result == 0) {
-                        return it.previousIndex();
-                    }
+                final int result = it.next().compareTo(object);
+                if (result == 0) {
+                    return it.previousIndex();
+                } else if (result > 0) {
                     return -it.previousIndex() - 1;
                 }
             }
             return -list.size() - 1;
         }
 
-        int low = 0, mid = list.size(), high = mid - 1, result = -1;
+        int low = 0, mid = list.size(), high = mid - 1, result = 1;
         while (low <= high) {
             mid = (low + high) >>> 1;
-            if ((result = -list.get(mid).compareTo(object)) > 0) {
+            result = list.get(mid).compareTo(object);
+            if (result < 0) {
                 low = mid + 1;
             } else if (result == 0) {
                 return mid;
@@ -1444,7 +1448,7 @@
                 high = mid - 1;
             }
         }
-        return -mid - (result < 0 ? 1 : 2);
+        return -mid - (result > 0 ? 1 : 2);
     }
 
     /**
@@ -1477,21 +1481,21 @@
         if (!(list instanceof RandomAccess)) {
             ListIterator<? extends T> it = list.listIterator();
             while (it.hasNext()) {
-                int result;
-                if ((result = -comparator.compare(it.next(), object)) <= 0) {
-                    if (result == 0) {
-                        return it.previousIndex();
-                    }
+                final int result = comparator.compare(it.next(), object);
+                if (result == 0) {
+                    return it.previousIndex();
+                } else if (result > 0) {
                     return -it.previousIndex() - 1;
                 }
             }
             return -list.size() - 1;
         }
 
-        int low = 0, mid = list.size(), high = mid - 1, result = -1;
+        int low = 0, mid = list.size(), high = mid - 1, result = 1;
         while (low <= high) {
             mid = (low + high) >>> 1;
-            if ((result = -comparator.compare(list.get(mid), object)) > 0) {
+            result = comparator.compare(list.get(mid), object);
+            if (result < 0) {
                 low = mid + 1;
             } else if (result == 0) {
                 return mid;
@@ -1499,7 +1503,7 @@
                 high = mid - 1;
             }
         }
-        return -mid - (result < 0 ? 1 : 2);
+        return -mid - (result > 0 ? 1 : 2);
     }
 
     /**
@@ -1860,13 +1864,23 @@
      */
     @SuppressWarnings("unchecked")
     public static <T extends Comparable<? super T>> void sort(List<T> list) {
-        Object[] array = list.toArray();
-        Arrays.sort(array);
-        int i = 0;
-        ListIterator<T> it = list.listIterator();
-        while (it.hasNext()) {
-            it.next();
-            it.set((T) array[i++]);
+        // Note that we can't use instanceof here since ArrayList isn't final and
+        // subclasses might make arbitrary use of array and modCount.
+        if (list.getClass() == ArrayList.class) {
+            ArrayList<T> arrayList = (ArrayList<T>) list;
+            Object[] array = arrayList.array;
+            int end = arrayList.size();
+            Arrays.sort(array, 0, end);
+            arrayList.modCount++;
+        } else {
+            Object[] array = list.toArray();
+            Arrays.sort(array);
+            int i = 0;
+            ListIterator<T> it = list.listIterator();
+            while (it.hasNext()) {
+                it.next();
+                it.set((T) array[i++]);
+            }
         }
     }
 
@@ -1879,13 +1893,21 @@
      */
     @SuppressWarnings("unchecked")
     public static <T> void sort(List<T> list, Comparator<? super T> comparator) {
-        T[] array = list.toArray((T[]) new Object[list.size()]);
-        Arrays.sort(array, comparator);
-        int i = 0;
-        ListIterator<T> it = list.listIterator();
-        while (it.hasNext()) {
-            it.next();
-            it.set(array[i++]);
+        if (list.getClass() == ArrayList.class) {
+            ArrayList<T> arrayList = (ArrayList<T>) list;
+            T[] array = (T[]) arrayList.array;
+            int end = arrayList.size();
+            Arrays.sort(array, 0, end, comparator);
+            arrayList.modCount++;
+        } else {
+            T[] array = list.toArray((T[]) new Object[list.size()]);
+            Arrays.sort(array, comparator);
+            int i = 0;
+            ListIterator<T> it = list.listIterator();
+            while (it.hasNext()) {
+                it.next();
+                it.set(array[i++]);
+            }
         }
     }
 
diff --git a/luni/src/main/java/java/util/Date.java b/luni/src/main/java/java/util/Date.java
index b4de055..d45c971 100644
--- a/luni/src/main/java/java/util/Date.java
+++ b/luni/src/main/java/java/util/Date.java
@@ -47,7 +47,10 @@
     private static final long serialVersionUID = 7523967970034938905L;
 
     // Used by parse()
-    private static final int CREATION_YEAR = new Date().getYear();
+    // Keep in a static inner class to allow compile-time initialization of Date.
+    private static class CreationYear {
+        private static final int VALUE = new Date().getYear();
+    }
 
     private transient long milliseconds;
 
@@ -539,7 +542,7 @@
             if (second == -1) {
                 second = 0;
             }
-            if (year < (CREATION_YEAR - 80)) {
+            if (year < (CreationYear.VALUE - 80)) {
                 year += 2000;
             } else if (year < 100) {
                 year += 1900;
diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java
index dfacb46..eee6ff4 100644
--- a/luni/src/main/java/java/util/EnumMap.java
+++ b/luni/src/main/java/java/util/EnumMap.java
@@ -36,9 +36,9 @@
 
     private Class<K> keyType;
 
-    transient Enum[] keys;
+    transient K[] keys;
 
-    transient Object[] values;
+    transient V[] values;
 
     transient boolean[] hasMapping;
 
@@ -48,8 +48,7 @@
 
     private transient EnumMapEntrySet<K, V> entrySet = null;
 
-    private static class Entry<KT extends Enum<KT>, VT> extends
-            MapEntry<KT, VT> {
+    private static class Entry<KT extends Enum<KT>, VT> extends MapEntry<KT, VT> {
         private final EnumMap<KT, VT> enumMap;
 
         private final int ordinal;
@@ -57,10 +56,9 @@
         Entry(KT theKey, VT theValue, EnumMap<KT, VT> em) {
             super(theKey, theValue);
             enumMap = em;
-            ordinal = ((Enum) theKey).ordinal();
+            ordinal = theKey.ordinal();
         }
 
-        @SuppressWarnings("unchecked")
         @Override
         public boolean equals(Object object) {
             if (!enumMap.hasMapping[ordinal]) {
@@ -68,7 +66,7 @@
             }
             boolean isEqual = false;
             if (object instanceof Map.Entry) {
-                Map.Entry<KT, VT> entry = (Map.Entry<KT, VT>) object;
+                Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
                 Object enumKey = entry.getKey();
                 if (key.equals(enumKey)) {
                     Object theValue = entry.getValue();
@@ -84,37 +82,32 @@
 
         @Override
         public int hashCode() {
-            return (enumMap.keys[ordinal] == null ? 0 : enumMap.keys[ordinal]
-                    .hashCode())
+            return (enumMap.keys[ordinal] == null ? 0 : enumMap.keys[ordinal].hashCode())
                     ^ (enumMap.values[ordinal] == null ? 0
                             : enumMap.values[ordinal].hashCode());
         }
 
-        @SuppressWarnings("unchecked")
         @Override
         public KT getKey() {
             checkEntryStatus();
-            return (KT) enumMap.keys[ordinal];
+            return enumMap.keys[ordinal];
         }
 
-        @SuppressWarnings("unchecked")
         @Override
         public VT getValue() {
             checkEntryStatus();
-            return (VT) enumMap.values[ordinal];
+            return enumMap.values[ordinal];
         }
 
-        @SuppressWarnings("unchecked")
         @Override
         public VT setValue(VT value) {
             checkEntryStatus();
-            return enumMap.put((KT) enumMap.keys[ordinal], value);
+            return enumMap.put(enumMap.keys[ordinal], value);
         }
 
         @Override
         public String toString() {
-            StringBuilder result = new StringBuilder(enumMap.keys[ordinal]
-                    .toString());
+            StringBuilder result = new StringBuilder(enumMap.keys[ordinal].toString());
             result.append("=");
             result.append(enumMap.values[ordinal] == null
                     ? "null" : enumMap.values[ordinal].toString());
@@ -128,8 +121,7 @@
         }
     }
 
-    private static class EnumMapIterator<E, KT extends Enum<KT>, VT> implements
-            Iterator<E> {
+    private static class EnumMapIterator<E, KT extends Enum<KT>, VT> implements Iterator<E> {
         int position = 0;
 
         int prePosition = -1;
@@ -153,13 +145,12 @@
             return position != length;
         }
 
-        @SuppressWarnings("unchecked")
         public E next() {
             if (!hasNext()) {
                 throw new NoSuchElementException();
             }
             prePosition = position++;
-            return type.get(new MapEntry(enumMap.keys[prePosition],
+            return type.get(new MapEntry<KT, VT>(enumMap.keys[prePosition],
                     enumMap.values[prePosition]));
         }
 
@@ -172,13 +163,12 @@
         }
 
         @Override
-        @SuppressWarnings("unchecked")
         public String toString() {
             if (prePosition == -1) {
                 return super.toString();
             }
             return type.get(
-                    new MapEntry(enumMap.keys[prePosition],
+                    new MapEntry<KT, VT>(enumMap.keys[prePosition],
                             enumMap.values[prePosition])).toString();
         }
 
@@ -208,8 +198,7 @@
         }
 
         @Override
-        @SuppressWarnings("unchecked")
-        public Iterator iterator() {
+        public Iterator<KT> iterator() {
             return new EnumMapIterator<KT, KT, VT>(
                     new MapEntry.Type<KT, KT, VT>() {
                         public KT get(MapEntry<KT, VT> entry) {
@@ -219,7 +208,6 @@
         }
 
         @Override
-        @SuppressWarnings("unchecked")
         public boolean remove(Object object) {
             if (contains(object)) {
                 enumMap.remove(object);
@@ -252,9 +240,8 @@
             return enumMap.containsValue(object);
         }
 
-        @SuppressWarnings("unchecked")
         @Override
-        public Iterator iterator() {
+        public Iterator<VT> iterator() {
             return new EnumMapIterator<VT, KT, VT>(
                     new MapEntry.Type<VT, KT, VT>() {
                         public VT get(MapEntry<KT, VT> entry) {
@@ -296,15 +283,14 @@
             super(value, em);
         }
 
-        @SuppressWarnings("unchecked")
         @Override
         public E next() {
             if (!hasNext()) {
                 throw new NoSuchElementException();
             }
             prePosition = position++;
-            return type.get(new Entry<KT, VT>((KT) enumMap.keys[prePosition],
-                    (VT) enumMap.values[prePosition], enumMap));
+            return type.get(new EnumMap.Entry<KT, VT>(enumMap.keys[prePosition],
+                    enumMap.values[prePosition], enumMap));
         }
     }
 
@@ -325,8 +311,8 @@
         public boolean contains(Object object) {
             boolean isEqual = false;
             if (object instanceof Map.Entry) {
-                Object enumKey = ((Map.Entry) object).getKey();
-                Object enumValue = ((Map.Entry) object).getValue();
+                Object enumKey = ((Map.Entry<?, ?>) object).getKey();
+                Object enumValue = ((Map.Entry<?, ?>) object).getValue();
                 if (enumMap.containsKey(enumKey)) {
                     VT value = enumMap.get(enumKey);
                     if (value == null) {
@@ -352,7 +338,7 @@
         @Override
         public boolean remove(Object object) {
             if (contains(object)) {
-                enumMap.remove(((Map.Entry) object).getKey());
+                enumMap.remove(((Map.Entry<?, ?>) object).getKey());
                 return true;
             }
             return false;
@@ -369,21 +355,22 @@
             return toArray(entryArray);
         }
 
-        @SuppressWarnings("unchecked")
         @Override
-        public Object[] toArray(Object[] array) {
+        public <T> T[] toArray(T[] array) {
             int size = enumMap.size();
             int index = 0;
-            Object[] entryArray = array;
+            T[] entryArray = array;
             if (size > array.length) {
                 Class<?> clazz = array.getClass().getComponentType();
-                entryArray = (Object[]) Array.newInstance(clazz, size);
+                @SuppressWarnings("unchecked") T[] newArray = (T[]) Array.newInstance(clazz, size);
+                entryArray = newArray;
             }
             Iterator<Map.Entry<KT, VT>> iter = iterator();
             for (; index < size; index++) {
                 Map.Entry<KT, VT> entry = iter.next();
-                entryArray[index] = new MapEntry<KT, VT>(entry.getKey(), entry
-                        .getValue());
+                @SuppressWarnings("unchecked") T newEntry =
+                        (T) new MapEntry<KT, VT>(entry.getKey(), entry.getValue());
+                entryArray[index] = newEntry;
             }
             if (index < array.length) {
                 entryArray[index] = null;
@@ -431,22 +418,27 @@
      * @throws NullPointerException
      *             if {@code map} is {@code null}.
      */
-    @SuppressWarnings("unchecked")
     public EnumMap(Map<K, ? extends V> map) {
         if (map instanceof EnumMap) {
-            initialization((EnumMap<K, V>) map);
+            @SuppressWarnings("unchecked") EnumMap<K, ? extends V> enumMap =
+                    (EnumMap<K, ? extends V>) map;
+            initialization(enumMap);
         } else {
             if (map.isEmpty()) {
                 throw new IllegalArgumentException("map is empty");
             }
             Iterator<K> iter = map.keySet().iterator();
             K enumKey = iter.next();
-            Class clazz = enumKey.getClass();
-            if (clazz.isEnum()) {
-                initialization(clazz);
-            } else {
-                initialization(clazz.getSuperclass());
+            // Confirm the key is actually an enum: Throw ClassCastException if not.
+            Enum.class.cast(enumKey);
+            Class<?> clazz = enumKey.getClass();
+            if (!clazz.isEnum()) {
+                // Each enum value can have its own subclass. In this case we want the abstract
+                // super-class which has the values() method.
+                clazz = clazz.getSuperclass();
             }
+            @SuppressWarnings("unchecked") Class<K> enumClass = (Class<K>) clazz;
+            initialization(enumClass);
             putAllImpl(map);
         }
     }
@@ -469,11 +461,10 @@
      *
      * @return a shallow copy of this {@code EnumMap}.
      */
-    @SuppressWarnings("unchecked")
     @Override
     public EnumMap<K, V> clone() {
         try {
-            EnumMap<K, V> enumMap = (EnumMap<K, V>) super.clone();
+            @SuppressWarnings("unchecked") EnumMap<K, V> enumMap = (EnumMap<K, V>) super.clone();
             enumMap.initialization(this);
             return enumMap;
         } catch (CloneNotSupportedException e) {
@@ -553,7 +544,6 @@
      * @see #hashCode()
      * @see #entrySet()
      */
-    @SuppressWarnings("unchecked")
     @Override
     public boolean equals(Object object) {
         if (this == object) {
@@ -562,7 +552,7 @@
         if (!(object instanceof EnumMap)) {
             return super.equals(object);
         }
-        EnumMap<K, V> enumMap = (EnumMap<K, V>) object;
+        @SuppressWarnings("unchecked") EnumMap<K, V> enumMap = (EnumMap<K, V>) object;
         if (keyType != enumMap.keyType || size() != enumMap.size()) {
             return false;
         }
@@ -579,13 +569,12 @@
      *         if no mapping for the specified key is found.
      */
     @Override
-    @SuppressWarnings("unchecked")
     public V get(Object key) {
         if (!isValidKeyType(key)) {
             return null;
         }
         int keyOrdinal = ((Enum) key).ordinal();
-        return (V) values[keyOrdinal];
+        return values[keyOrdinal];
     }
 
     /**
@@ -627,7 +616,6 @@
      *                support {@code null} keys or values.
      */
     @Override
-    @SuppressWarnings("unchecked")
     public V put(K key, V value) {
         return putImpl(key, value);
     }
@@ -649,7 +637,6 @@
      *                support {@code null} keys or values.
      */
     @Override
-    @SuppressWarnings("unchecked")
     public void putAll(Map<? extends K, ? extends V> map) {
         putAllImpl(map);
     }
@@ -665,7 +652,6 @@
      *                if removing from this {@code EnumMap} is not supported.
      */
     @Override
-    @SuppressWarnings("unchecked")
     public V remove(Object key) {
         if (!isValidKeyType(key)) {
             return null;
@@ -675,7 +661,7 @@
             hasMapping[keyOrdinal] = false;
             mappingsCount--;
         }
-        V oldValue = (V) values[keyOrdinal];
+        V oldValue = values[keyOrdinal];
         values[keyOrdinal] = null;
         return oldValue;
     }
@@ -716,35 +702,29 @@
         stream.defaultReadObject();
         initialization(keyType);
         int elementCount = stream.readInt();
-        Enum<K> enumKey;
-        Object value;
+        K enumKey;
+        V value;
         for (int i = elementCount; i > 0; i--) {
-            enumKey = (Enum<K>) stream.readObject();
-            value = stream.readObject();
-            putImpl((K) enumKey, (V) value);
+            enumKey = (K) stream.readObject();
+            value = (V) stream.readObject();
+            putImpl(enumKey, value);
         }
     }
 
     private void writeObject(ObjectOutputStream stream) throws IOException {
         stream.defaultWriteObject();
         stream.writeInt(mappingsCount);
-        Iterator<Map.Entry<K, V>> iterator = entrySet().iterator();
-        while (iterator.hasNext()) {
-            Map.Entry<K, V> entry = iterator.next();
+        for (Map.Entry<K, V> entry : entrySet()) {
             stream.writeObject(entry.getKey());
             stream.writeObject(entry.getValue());
         }
     }
 
     private boolean isValidKeyType(Object key) {
-        if (key != null && keyType.isInstance(key)) {
-            return true;
-        }
-        return false;
+        return key != null && keyType.isInstance(key);
     }
 
-    @SuppressWarnings("unchecked")
-    private void initialization(EnumMap enumMap) {
+    private void initialization(EnumMap<K, ? extends V> enumMap) {
         keyType = enumMap.keyType;
         keys = enumMap.keys;
         enumSize = enumMap.enumSize;
@@ -757,20 +737,19 @@
         keyType = type;
         keys = Enum.getSharedConstants(keyType);
         enumSize = keys.length;
-        values = new Object[enumSize];
+        // The value array is actually Object[] for speed of creation. It is treated as a V[]
+        // because it is safe to do so and eliminates unchecked warning suppression throughout.
+        @SuppressWarnings("unchecked") V[] valueArray = (V[]) new Object[enumSize];
+        values = valueArray;
         hasMapping = new boolean[enumSize];
     }
 
-    @SuppressWarnings("unchecked")
-    private void putAllImpl(Map map) {
-        Iterator iter = map.entrySet().iterator();
-        while (iter.hasNext()) {
-            Map.Entry entry = (Map.Entry) iter.next();
-            putImpl((K) entry.getKey(), (V) entry.getValue());
+    private void putAllImpl(Map<? extends K, ? extends V> map) {
+        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+            putImpl(entry.getKey(), entry.getValue());
         }
     }
 
-    @SuppressWarnings("unchecked")
     private V putImpl(K key, V value) {
         if (key == null) {
             throw new NullPointerException("key == null");
@@ -781,7 +760,7 @@
             hasMapping[keyOrdinal] = true;
             mappingsCount++;
         }
-        V oldValue = (V) values[keyOrdinal];
+        V oldValue = values[keyOrdinal];
         values[keyOrdinal] = value;
         return oldValue;
     }
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index a6368e8..1885f83 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -272,17 +272,77 @@
      */
     private static final String UNDETERMINED_LANGUAGE = "und";
 
+
     /**
-     * The current default locale. It is temporarily assigned to US because we
-     * need a default locale to lookup the real default locale.
+     * Map of grandfathered language tags to their modern replacements.
      */
-    private static Locale defaultLocale = US;
+    private static final TreeMap<String, String> GRANDFATHERED_LOCALES;
 
     static {
-        String language = System.getProperty("user.language", "en");
-        String region = System.getProperty("user.region", "US");
-        String variant = System.getProperty("user.variant", "");
-        defaultLocale = new Locale(language, region, variant);
+        GRANDFATHERED_LOCALES = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+
+        // From http://tools.ietf.org/html/bcp47
+        //
+        // grandfathered = irregular           ; non-redundant tags registered
+        //               / regular             ; during the RFC 3066 era
+        //  irregular =
+        GRANDFATHERED_LOCALES.put("en-GB-oed", "en-GB-x-oed");
+        GRANDFATHERED_LOCALES.put("i-ami", "ami");
+        GRANDFATHERED_LOCALES.put("i-bnn", "bnn");
+        GRANDFATHERED_LOCALES.put("i-default", "en-x-i-default");
+        GRANDFATHERED_LOCALES.put("i-enochian", "und-x-i-enochian");
+        GRANDFATHERED_LOCALES.put("i-hak", "hak");
+        GRANDFATHERED_LOCALES.put("i-klingon", "tlh");
+        GRANDFATHERED_LOCALES.put("i-lux", "lb");
+        GRANDFATHERED_LOCALES.put("i-mingo", "see-x-i-mingo");
+        GRANDFATHERED_LOCALES.put("i-navajo", "nv");
+        GRANDFATHERED_LOCALES.put("i-pwn", "pwn");
+        GRANDFATHERED_LOCALES.put("i-tao", "tao");
+        GRANDFATHERED_LOCALES.put("i-tay", "tay");
+        GRANDFATHERED_LOCALES.put("i-tsu", "tsu");
+        GRANDFATHERED_LOCALES.put("sgn-BE-FR", "sfb");
+        GRANDFATHERED_LOCALES.put("sgn-BE-NL", "vgt");
+        GRANDFATHERED_LOCALES.put("sgn-CH-DE", "sgg");
+
+        // regular =
+        GRANDFATHERED_LOCALES.put("art-lojban", "jbo");
+        GRANDFATHERED_LOCALES.put("cel-gaulish", "xtg-x-cel-gaulish");
+        GRANDFATHERED_LOCALES.put("no-bok", "nb");
+        GRANDFATHERED_LOCALES.put("no-nyn", "nn");
+        GRANDFATHERED_LOCALES.put("zh-guoyu", "cmn");
+        GRANDFATHERED_LOCALES.put("zh-hakka", "hak");
+        GRANDFATHERED_LOCALES.put("zh-min", "nan-x-zh-min");
+        GRANDFATHERED_LOCALES.put("zh-min-nan", "nan");
+        GRANDFATHERED_LOCALES.put("zh-xiang", "hsn");
+    }
+
+    private static class NoImagePreloadHolder {
+        /**
+         * The default locale, returned by {@code Locale.getDefault()}.
+         * Initialize the default locale from the system properties.
+         */
+        private static Locale defaultLocale = Locale.getDefaultLocaleFromSystemProperties();
+    }
+
+    /**
+     * Returns the default locale from system properties.
+     *
+     * @hide visible for testing.
+     */
+    public static Locale getDefaultLocaleFromSystemProperties() {
+        final String languageTag = System.getProperty("user.locale", "");
+
+        final Locale defaultLocale;
+        if (!languageTag.isEmpty()) {
+            defaultLocale = Locale.forLanguageTag(languageTag);
+        } else {
+            String language = System.getProperty("user.language", "en");
+            String region = System.getProperty("user.region", "US");
+            String variant = System.getProperty("user.variant", "");
+            defaultLocale = new Locale(language, region, variant);
+        }
+
+        return defaultLocale;
     }
 
     /**
@@ -1006,7 +1066,7 @@
      * Instead, use this method to look it up for each use.
      */
     public static Locale getDefault() {
-        return defaultLocale;
+        return NoImagePreloadHolder.defaultLocale;
     }
 
     /**
@@ -1600,7 +1660,7 @@
             throw new NullPointerException("locale == null");
         }
         String languageTag = locale.toLanguageTag();
-        defaultLocale = locale;
+        NoImagePreloadHolder.defaultLocale = locale;
         ICU.setDefaultLocale(languageTag);
     }
 
@@ -2020,49 +2080,6 @@
         return adjusted;
     }
 
-    /**
-     * Map of grandfathered language tags to their modern replacements.
-     */
-    private static final TreeMap<String, String> GRANDFATHERED_LOCALES;
-
-    static {
-        GRANDFATHERED_LOCALES = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
-
-        // From http://tools.ietf.org/html/bcp47
-        //
-        // grandfathered = irregular           ; non-redundant tags registered
-        //               / regular             ; during the RFC 3066 era
-        //  irregular =
-        GRANDFATHERED_LOCALES.put("en-GB-oed", "en-GB-x-oed");
-        GRANDFATHERED_LOCALES.put("i-ami", "ami");
-        GRANDFATHERED_LOCALES.put("i-bnn", "bnn");
-        GRANDFATHERED_LOCALES.put("i-default", "en-x-i-default");
-        GRANDFATHERED_LOCALES.put("i-enochian", "und-x-i-enochian");
-        GRANDFATHERED_LOCALES.put("i-hak", "hak");
-        GRANDFATHERED_LOCALES.put("i-klingon", "tlh");
-        GRANDFATHERED_LOCALES.put("i-lux", "lb");
-        GRANDFATHERED_LOCALES.put("i-mingo", "see-x-i-mingo");
-        GRANDFATHERED_LOCALES.put("i-navajo", "nv");
-        GRANDFATHERED_LOCALES.put("i-pwn", "pwn");
-        GRANDFATHERED_LOCALES.put("i-tao", "tao");
-        GRANDFATHERED_LOCALES.put("i-tay", "tay");
-        GRANDFATHERED_LOCALES.put("i-tsu", "tsu");
-        GRANDFATHERED_LOCALES.put("sgn-BE-FR", "sfb");
-        GRANDFATHERED_LOCALES.put("sgn-BE-NL", "vgt");
-        GRANDFATHERED_LOCALES.put("sgn-CH-DE", "sgg");
-
-        // regular =
-        GRANDFATHERED_LOCALES.put("art-lojban", "jbo");
-        GRANDFATHERED_LOCALES.put("cel-gaulish", "xtg-x-cel-gaulish");
-        GRANDFATHERED_LOCALES.put("no-bok", "nb");
-        GRANDFATHERED_LOCALES.put("no-nyn", "nn");
-        GRANDFATHERED_LOCALES.put("zh-guoyu", "cmn");
-        GRANDFATHERED_LOCALES.put("zh-hakka", "hak");
-        GRANDFATHERED_LOCALES.put("zh-min", "nan-x-zh-min");
-        GRANDFATHERED_LOCALES.put("zh-min-nan", "nan");
-        GRANDFATHERED_LOCALES.put("zh-xiang", "hsn");
-    }
-
     private static String convertGrandfatheredTag(String original) {
         final String converted = GRANDFATHERED_LOCALES.get(original);
         return converted != null ? converted : original;
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index 854a4a6..d7beb91 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -68,7 +68,51 @@
 public abstract class TimeZone implements Serializable, Cloneable {
     private static final long serialVersionUID = 3581463369166924961L;
 
-    private static final Pattern CUSTOM_ZONE_ID_PATTERN = Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$");
+    /**
+     * Helper class to parse a custom timezone. This is in a separate class as regular expressions
+     * cannot be compile-time initialized, so that static field is separated out from TimeZone
+     * proper.
+     */
+    private final static class CustomTimeZoneParser {
+        private static final Pattern CUSTOM_ZONE_ID_PATTERN =
+                Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$");
+
+        private CustomTimeZoneParser() {}
+
+        /**
+         * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
+         */
+        private static TimeZone getCustomTimeZone(String id) {
+            Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
+            if (!m.matches()) {
+                return null;
+            }
+
+            int hour;
+            int minute = 0;
+            try {
+                hour = Integer.parseInt(m.group(1));
+                if (m.group(3) != null) {
+                    minute = Integer.parseInt(m.group(3));
+                }
+            } catch (NumberFormatException impossible) {
+                throw new AssertionError(impossible);
+            }
+
+            if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
+                return null;
+            }
+
+            char sign = id.charAt(3);
+            int raw = (hour * 3600000) + (minute * 60000);
+            if (sign == '-') {
+                raw = -raw;
+            }
+
+            String cleanId = String.format((Locale) null, "GMT%c%02d:%02d", sign, hour, minute);
+            return new SimpleTimeZone(raw, cleanId);
+        }
+    }
 
     /**
      * The short display name style, such as {@code PDT}. Requests for this
@@ -368,7 +412,7 @@
 
         // Custom time zone?
         if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
-            zone = getCustomTimeZone(id);
+            zone = CustomTimeZoneParser.getCustomTimeZone(id);
         }
 
         // We never return null; on failure we return the equivalent of "GMT".
@@ -376,40 +420,6 @@
     }
 
     /**
-     * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
-     */
-    private static TimeZone getCustomTimeZone(String id) {
-        Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
-        if (!m.matches()) {
-            return null;
-        }
-
-        int hour;
-        int minute = 0;
-        try {
-            hour = Integer.parseInt(m.group(1));
-            if (m.group(3) != null) {
-                minute = Integer.parseInt(m.group(3));
-            }
-        } catch (NumberFormatException impossible) {
-            throw new AssertionError(impossible);
-        }
-
-        if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
-            return null;
-        }
-
-        char sign = id.charAt(3);
-        int raw = (hour * 3600000) + (minute * 60000);
-        if (sign == '-') {
-            raw = -raw;
-        }
-
-        String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute);
-        return new SimpleTimeZone(raw, cleanId);
-    }
-
-    /**
      * Returns true if {@code timeZone} has the same rules as this time zone.
      *
      * <p>The base implementation returns true if both time zones have the same
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
index 803cd49..4a76104 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
@@ -293,11 +293,13 @@
 
     private static final long serialVersionUID = -8627078645895051609L;
 
-    /**
-     * Generates the initial random seed for the cheaper per-instance
-     * random number generators used in randomLevel.
-     */
-    private static final Random seedGenerator = new Random();
+//  BEGIN android-removed
+//  /**
+//   * Generates the initial random seed for the cheaper per-instance
+//   * random number generators used in randomLevel.
+//   */
+//  private static final Random seedGenerator = new Random();
+//  END android-removed
 
     /**
      * Special value used to identify base-level header
@@ -341,7 +343,13 @@
         entrySet = null;
         values = null;
         descendingMap = null;
-        randomSeed = seedGenerator.nextInt() | 0x0100; // ensure nonzero
+        // BEGIN android-changed
+        //
+        // Most processes are forked from the zygote, so they'll end up
+        // with the same random seed unless we take additional post fork
+        // measures.
+        randomSeed = Math.randomIntInternal() | 0x0100; // ensure nonzero
+        // END android-changed
         head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
                                   null, null, 1);
     }
diff --git a/luni/src/main/java/java/util/jar/Manifest.java b/luni/src/main/java/java/util/jar/Manifest.java
index 6a3936d..5a6b42d 100644
--- a/luni/src/main/java/java/util/jar/Manifest.java
+++ b/luni/src/main/java/java/util/jar/Manifest.java
@@ -41,8 +41,10 @@
 
     private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
 
-    private final Attributes mainAttributes;
-    private final HashMap<String, Attributes> entries;
+    /* non-final for {@code #clone()} */
+    private Attributes mainAttributes;
+    /* non-final for {@code #clone()} */
+    private HashMap<String, Attributes> entries;
 
     static final class Chunk {
         final int start;
@@ -93,9 +95,7 @@
      */
     @SuppressWarnings("unchecked")
     public Manifest(Manifest man) {
-        mainAttributes = (Attributes) man.mainAttributes.clone();
-        entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man
-                .getEntries()).clone();
+        cloneAttributesAndEntriesFrom(man);
     }
 
     Manifest(byte[] manifestBytes, boolean readChunks) throws IOException {
@@ -156,7 +156,21 @@
      */
     @Override
     public Object clone() {
-        return new Manifest(this);
+        Manifest result;
+        try {
+            result = (Manifest) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(e);
+        }
+
+        result.cloneAttributesAndEntriesFrom(this);
+        return result;
+    }
+
+    private final void cloneAttributesAndEntriesFrom(Manifest other) {
+        mainAttributes = (Attributes) other.mainAttributes.clone();
+        entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) other
+                .getEntries()).clone();
     }
 
     /**
diff --git a/luni/src/main/java/java/util/jar/StrictJarFile.java b/luni/src/main/java/java/util/jar/StrictJarFile.java
index 4a8af5f..a73ca2a 100644
--- a/luni/src/main/java/java/util/jar/StrictJarFile.java
+++ b/luni/src/main/java/java/util/jar/StrictJarFile.java
@@ -24,6 +24,7 @@
 import java.security.cert.Certificate;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Set;
 import java.util.zip.Inflater;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -53,7 +54,7 @@
     private final CloseGuard guard = CloseGuard.get();
     private boolean closed;
 
-    public StrictJarFile(String fileName) throws IOException {
+    public StrictJarFile(String fileName) throws IOException, SecurityException {
         this.nativeHandle = nativeOpenJarFile(fileName);
         this.raf = new RandomAccessFile(fileName, "r");
 
@@ -64,11 +65,18 @@
             HashMap<String, byte[]> metaEntries = getMetaEntries();
             this.manifest = new Manifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
             this.verifier = new JarVerifier(fileName, manifest, metaEntries);
+            Set<String> files = this.manifest.getEntries().keySet();
+            for (String file : files) {
+                if (findEntry(file) == null) {
+                    throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+                }
+            }
 
             isSigned = verifier.readCertificates() && verifier.isSignedJar();
-        } catch (IOException ioe) {
+        } catch (IOException | SecurityException e) {
             nativeClose(this.nativeHandle);
-            throw ioe;
+            IoUtils.closeQuietly(this.raf);
+            throw e;
         }
 
         guard.open("close");
diff --git a/luni/src/main/java/java/util/logging/FileHandler.java b/luni/src/main/java/java/util/logging/FileHandler.java
index 6ffef87..1fd1dbf 100644
--- a/luni/src/main/java/java/util/logging/FileHandler.java
+++ b/luni/src/main/java/java/util/logging/FileHandler.java
@@ -261,15 +261,15 @@
         boolean hasUniqueID = false;
         boolean hasGeneration = false;
 
-        // TODO privilege code?
+        String homePath = System.getProperty("user.home");
+        if (homePath == null) {
+            throw new NullPointerException("System property \"user.home\" is null");
+        }
+        boolean homePathHasSepEnd = homePath.endsWith(File.separator);
 
         String tempPath = System.getProperty("java.io.tmpdir");
-        boolean tempPathHasSepEnd = (tempPath == null ? false : tempPath
-                .endsWith(File.separator));
-
-        String homePath = System.getProperty("user.home");
-        boolean homePathHasSepEnd = (homePath == null ? false : homePath
-                .endsWith(File.separator));
+        tempPath = tempPath == null ? homePath : tempPath;
+        boolean tempPathHasSepEnd = tempPath.endsWith(File.separator);
 
         StringBuilder sb = new StringBuilder();
         pattern = pattern.replace('/', File.separatorChar);
diff --git a/luni/src/main/java/java/util/logging/SocketHandler.java b/luni/src/main/java/java/util/logging/SocketHandler.java
index 48bfc0e..5de847a 100644
--- a/luni/src/main/java/java/util/logging/SocketHandler.java
+++ b/luni/src/main/java/java/util/logging/SocketHandler.java
@@ -17,6 +17,7 @@
 
 package java.util.logging;
 
+import libcore.net.NetworkSecurityPolicy;
 import java.io.BufferedOutputStream;
 import java.io.IOException;
 import java.net.Socket;
@@ -106,14 +107,21 @@
             throw new IllegalArgumentException("host == null || host.isEmpty()");
         }
         // check the validity of the port number
-        int p = 0;
+        int p;
         try {
             p = Integer.parsePositiveInt(port);
+            // Must be >= 0 to get this far. 0 is invalid too.
+            if (p == 0) {
+                throw new IllegalArgumentException("Illegal port argument " + port);
+            }
         } catch (NumberFormatException e) {
             throw new IllegalArgumentException("Illegal port argument " + port);
         }
         // establish the network connection
         try {
+            if (!NetworkSecurityPolicy.isCleartextTrafficPermitted()) {
+                throw new IOException("Cleartext traffic not permitted");
+            }
             this.socket = new Socket(host, p);
         } catch (IOException e) {
             getErrorManager().error("Failed to establish the network connection", e,
diff --git a/luni/src/main/java/java/util/logging/XMLFormatter.java b/luni/src/main/java/java/util/logging/XMLFormatter.java
index 0d80b3e..3952596 100644
--- a/luni/src/main/java/java/util/logging/XMLFormatter.java
+++ b/luni/src/main/java/java/util/logging/XMLFormatter.java
@@ -17,6 +17,7 @@
 
 package java.util.logging;
 
+import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.Date;
 import java.util.ResourceBundle;
@@ -50,7 +51,7 @@
         // call a method of LogRecord to ensure not null
         long time = r.getMillis();
         // format to date
-        String date = MessageFormat.format("{0, date} {0, time}", new Object[] { new Date(time) });
+        String date = MessageFormat.format("{0, date} {0, time}", new Date(time));
         String nl = System.lineSeparator();
 
         StringBuilder sb = new StringBuilder();
@@ -59,21 +60,21 @@
         append(sb, 1, "millis", time);
         append(sb, 1, "sequence", r.getSequenceNumber());
         if (r.getLoggerName() != null) {
-            append(sb, 1, "logger", r.getLoggerName());
+            escapeAndAppend(sb, 1, "logger", r.getLoggerName());
         }
         append(sb, 1, "level", r.getLevel().getName());
         if (r.getSourceClassName() != null) {
             append(sb, 1, "class", r.getSourceClassName());
         }
         if (r.getSourceMethodName() != null) {
-            append(sb, 1, "method", r.getSourceMethodName());
+            escapeAndAppend(sb, 1, "method", r.getSourceMethodName());
         }
         append(sb, 1, "thread", r.getThreadID());
         formatMessages(r, sb);
         Object[] params = r.getParameters();
         if (params != null) {
             for (Object element : params) {
-                append(sb, 1, "param", element);
+                escapeAndAppend(sb, 1, "param", element);
             }
         }
         formatThrowable(r, sb);
@@ -96,14 +97,14 @@
 
             if (message == null) {
                 message = pattern;
-                append(sb, 1, "message", message);
+                escapeAndAppend(sb, 1, "message", message);
             } else {
-                append(sb, 1, "message", message);
-                append(sb, 1, "key", pattern);
-                append(sb, 1, "catalog", r.getResourceBundleName());
+                escapeAndAppend(sb, 1, "message", message);
+                escapeAndAppend(sb, 1, "key", pattern);
+                escapeAndAppend(sb, 1, "catalog", r.getResourceBundleName());
             }
         } else if (pattern != null) {
-            append(sb, 1, "message", pattern);
+            escapeAndAppend(sb, 1, "message", pattern);
         } else {
             sb.append(indent).append("<message/>");
         }
@@ -114,13 +115,13 @@
         if ((t = r.getThrown()) != null) {
             String nl = System.lineSeparator();
             sb.append(indent).append("<exception>").append(nl);
-            append(sb, 2, "message", t.toString());
+            escapeAndAppend(sb, 2, "message", t.toString());
             // format throwable's stack trace
             StackTraceElement[] elements = t.getStackTrace();
             for (StackTraceElement e : elements) {
                 sb.append(indent).append(indent).append("<frame>").append(nl);
                 append(sb, 3, "class", e.getClassName());
-                append(sb, 3, "method", e.getMethodName());
+                escapeAndAppend(sb, 3, "method", e.getMethodName());
                 append(sb, 3, "line", e.getLineNumber());
                 sb.append(indent).append(indent).append("</frame>").append(nl);
             }
@@ -138,6 +139,49 @@
         sb.append(System.lineSeparator());
     }
 
+    private static void escapeAndAppend(StringBuilder sb, int indentCount, String tag, Object value) {
+        if (value == null) {
+            append(sb, indentCount, tag, value);
+        } else {
+            for (int i = 0; i < indentCount; ++i) {
+                sb.append(indent);
+            }
+            sb.append("<").append(tag).append(">");
+            try {
+                escapeXml(sb, value.toString());
+            } catch (IOException e) {
+                throw new AssertionError();
+            }
+            sb.append("</").append(tag).append(">");
+            sb.append(System.lineSeparator());
+        }
+    }
+
+    private static void escapeXml(Appendable valueBuilder, String value) throws IOException {
+        for (int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            switch (c) {
+                case '\"':
+                    valueBuilder.append("&quot;");
+                    break;
+                case '>':
+                    valueBuilder.append("&gt;");
+                    break;
+                case '<':
+                    valueBuilder.append("&lt;");
+                    break;
+                case '&':
+                    valueBuilder.append("&amp;");
+                    break;
+                case '\'':
+                    valueBuilder.append("&apos;");
+                    break;
+                default:
+                    valueBuilder.append(c);
+            }
+        }
+    }
+
     /**
      * Returns the header string for a set of log records formatted as XML
      * strings, using the output handler's encoding if it is defined, otherwise
diff --git a/luni/src/main/java/java/util/zip/GZIPInputStream.java b/luni/src/main/java/java/util/zip/GZIPInputStream.java
index 1bfc496..925e8c4 100644
--- a/luni/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/luni/src/main/java/java/util/zip/GZIPInputStream.java
@@ -222,7 +222,7 @@
             if (hcrc) {
                 crc.update(header, 0, 2);
             }
-            int length = Memory.peekShort(scratch, 0, ByteOrder.LITTLE_ENDIAN) & 0xffff;
+            int length = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN) & 0xffff;
             while (length > 0) {
                 int max = length > scratch.length ? scratch.length : length;
                 int result = in.read(scratch, 0, max);
@@ -243,7 +243,7 @@
         }
         if (hcrc) {
             Streams.readFully(in, header, 0, 2);
-            short crc16 = Memory.peekShort(scratch, 0, ByteOrder.LITTLE_ENDIAN);
+            short crc16 = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN);
             if ((short) crc.getValue() != crc16) {
                 throw new IOException("CRC mismatch");
             }
diff --git a/luni/src/main/java/java/util/zip/Zip64.java b/luni/src/main/java/java/util/zip/Zip64.java
new file mode 100644
index 0000000..9be3d1c
--- /dev/null
+++ b/luni/src/main/java/java/util/zip/Zip64.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2015 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.util.zip;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static java.util.zip.ZipOutputStream.writeIntAsUint16;
+import static java.util.zip.ZipOutputStream.writeLongAsUint32;
+import static java.util.zip.ZipOutputStream.writeLongAsUint64;
+
+/**
+ * @hide
+ */
+public class Zip64 {
+
+    /* Non instantiable */
+    private Zip64() {}
+
+    /**
+     * The maximum supported entry / archive size for standard (non zip64) entries and archives.
+     */
+    static final long MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE = 0x00000000ffffffffL;
+
+    /**
+     * The header ID of the zip64 extended info header. This value is used to identify
+     * zip64 data in the "extra" field in the file headers.
+     */
+    private static final short ZIP64_EXTENDED_INFO_HEADER_ID = 0x0001;
+
+    /**
+     * The minimum size of the zip64 extended info header. This excludes the 2 byte header ID
+     * and the 2 byte size.
+     */
+    private static final int ZIP64_EXTENDED_INFO_MIN_SIZE = 28;
+
+    /*
+     * Size (in bytes) of the zip64 end of central directory locator. This will be located
+     * immediately before the end of central directory record if a given zipfile is in the
+     * zip64 format.
+     */
+    private static final int ZIP64_LOCATOR_SIZE = 20;
+
+    /**
+     * The zip64 end of central directory locator signature (4 bytes wide).
+     */
+    private static final int ZIP64_LOCATOR_SIGNATURE = 0x07064b50;
+
+    /**
+     * The zip64 end of central directory record singature (4 bytes wide).
+     */
+    private static final int ZIP64_EOCD_RECORD_SIGNATURE = 0x06064b50;
+
+    /**
+     * The "effective" size of the zip64 eocd record. This excludes the fields that
+     * are proprietary, signature, or fields we aren't interested in. We include the
+     * following (contiguous) fields in this calculation :
+     * - disk number (4 bytes)
+     * - disk with start of central directory (4 bytes)
+     * - number of central directory entries on this disk (8 bytes)
+     * - total number of central directory entries (8 bytes)
+     * - size of the central directory (8 bytes)
+     * - offset of the start of the central directory (8 bytes)
+     */
+    private static final int ZIP64_EOCD_RECORD_EFFECTIVE_SIZE = 40;
+
+    /**
+     * Parses the zip64 end of central directory record locator. The locator
+     * must be placed immediately before the end of central directory (eocd) record
+     * starting at {@code eocdOffset}.
+     *
+     * The position of the file cursor for {@code raf} after a call to this method
+     * is undefined an callers must reposition it after each call to this method.
+     */
+    public static long parseZip64EocdRecordLocator(RandomAccessFile raf, long eocdOffset)
+            throws IOException {
+        // The spec stays curiously silent about whether a zip file with an EOCD record,
+        // a zip64 locator and a zip64 eocd record is considered "empty". In our implementation,
+        // we parse all records and read the counts from them instead of drawing any size or
+        // layout based information.
+        if (eocdOffset > ZIP64_LOCATOR_SIZE) {
+            raf.seek(eocdOffset - ZIP64_LOCATOR_SIZE);
+            if (Integer.reverseBytes(raf.readInt()) == ZIP64_LOCATOR_SIGNATURE) {
+                byte[] zip64EocdLocator = new byte[ZIP64_LOCATOR_SIZE  - 4];
+                raf.readFully(zip64EocdLocator);
+                ByteBuffer buf = ByteBuffer.wrap(zip64EocdLocator).order(ByteOrder.LITTLE_ENDIAN);
+
+                final int diskWithCentralDir = buf.getInt();
+                final long zip64EocdRecordOffset = buf.getLong();
+                final int numDisks = buf.getInt();
+
+                if (numDisks != 1 || diskWithCentralDir != 0) {
+                    throw new ZipException("Spanned archives not supported");
+                }
+
+                return zip64EocdRecordOffset;
+            }
+        }
+
+        return -1;
+    }
+
+    public static ZipFile.EocdRecord parseZip64EocdRecord(RandomAccessFile raf,
+            long eocdRecordOffset, int commentLength) throws IOException {
+        raf.seek(eocdRecordOffset);
+        final int signature = Integer.reverseBytes(raf.readInt());
+        if (signature != ZIP64_EOCD_RECORD_SIGNATURE) {
+            throw new ZipException("Invalid zip64 eocd record offset, sig="
+                    + Integer.toHexString(signature) + " offset=" + eocdRecordOffset);
+        }
+
+        // The zip64 eocd record specifies its own size as an 8 byte integral type. It is variable
+        // length because of the "zip64 extensible data sector" but that field is reserved for
+        // pkware's proprietary use. We therefore disregard it altogether and treat the end of
+        // central directory structure as fixed length.
+        //
+        // We also skip "version made by" (2 bytes) and "version needed to extract" (2 bytes)
+        // fields. We perform additional validation at the ZipEntry level, where applicable.
+        //
+        // That's a total of 12 bytes to skip
+        raf.skipBytes(12);
+
+        byte[] zip64Eocd = new byte[ZIP64_EOCD_RECORD_EFFECTIVE_SIZE];
+        raf.readFully(zip64Eocd);
+
+        ByteBuffer buf = ByteBuffer.wrap(zip64Eocd).order(ByteOrder.LITTLE_ENDIAN);
+        try {
+            int diskNumber = buf.getInt();
+            int diskWithCentralDirStart = buf.getInt();
+            long numEntries = buf.getLong();
+            long totalNumEntries = buf.getLong();
+            buf.getLong(); // Ignore the size of the central directory
+            long centralDirOffset = buf.getLong();
+
+            if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDirStart != 0) {
+                throw new ZipException("Spanned archives not supported :" +
+                        " numEntries=" + numEntries + ", totalNumEntries=" + totalNumEntries +
+                        ", diskNumber=" + diskNumber + ", diskWithCentralDirStart=" +
+                        diskWithCentralDirStart);
+            }
+
+            return new ZipFile.EocdRecord(numEntries, centralDirOffset, commentLength);
+        } catch (BufferUnderflowException bue) {
+            ZipException zipException = new ZipException("Error parsing zip64 eocd record.");
+            zipException.initCause(bue);
+            throw zipException;
+        }
+    }
+
+    /**
+     * Parse the zip64 extended info record from the extras present in {@code ze}.
+     *
+     * If {@code fromCentralDirectory} is true, we assume we're parsing a central directory
+     * record. We assume a local file header otherwise. The difference between the two is that
+     * a central directory entry is required to be complete, whereas a local file header isn't.
+     * This is due to the presence of an optional data descriptor after the file content.
+     *
+     * @return {@code} true iff. a zip64 extended info record was found.
+     */
+    public static boolean parseZip64ExtendedInfo(ZipEntry ze, boolean fromCentralDirectory)
+            throws ZipException {
+        int extendedInfoSize = -1;
+        int extendedInfoStart = -1;
+        // If this file contains a zip64 central directory locator, entries might
+        // optionally contain a zip64 extended information extra entry.
+        if (ze.extra != null && ze.extra.length > 0) {
+            // Extensible data fields are of the form header1+data1 + header2+data2 and so
+            // on, where each header consists of a 2 byte header ID followed by a 2 byte size.
+            // We need to iterate through the entire list of headers to find the header ID
+            // for the zip64 extended information extra field (0x0001).
+            final ByteBuffer buf = ByteBuffer.wrap(ze.extra).order(ByteOrder.LITTLE_ENDIAN);
+            extendedInfoSize = getZip64ExtendedInfoSize(buf);
+            if (extendedInfoSize != -1) {
+                extendedInfoStart = buf.position();
+                try {
+                    if (extendedInfoSize < ZIP64_EXTENDED_INFO_MIN_SIZE) {
+                        throw new ZipException("Invalid zip64 extended info size: " + extendedInfoSize);
+                    }
+
+                    // The size & compressed size only make sense in the central directory *or* if
+                    // we know them beforehand. If we don't know them beforehand, they're stored in
+                    // the data descriptor and should be read from there.
+                    if (fromCentralDirectory || (ze.getMethod() == ZipEntry.STORED)) {
+                        final long zip64Size = buf.getLong();
+                        if (ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+                            ze.size = zip64Size;
+                        }
+
+                        final long zip64CompressedSize = buf.getLong();
+                        if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+                            ze.compressedSize = zip64CompressedSize;
+                        }
+                    }
+
+                    // The local header offset is significant only in the central directory. It makes no
+                    // sense within the local header itself.
+                    if (fromCentralDirectory) {
+                        final long zip64LocalHeaderRelOffset = buf.getLong();
+                        if (ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+                            ze.localHeaderRelOffset = zip64LocalHeaderRelOffset;
+                        }
+                    }
+                } catch (BufferUnderflowException bue) {
+                    ZipException zipException = new ZipException("Error parsing extendend info ");
+                    zipException.initCause(bue);
+                    throw zipException;
+                }
+            }
+        }
+
+        // This entry doesn't contain a zip64 extended information data entry header.
+        // We have to check that the compressedSize / size / localHeaderRelOffset values
+        // are valid and don't require the presence of the extended header.
+        if (extendedInfoSize == -1) {
+            if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE ||
+                    ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE ||
+                    ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+                throw new ZipException("File contains no zip64 extended information: "
+                        + "name=" + ze.name + "compressedSize=" + ze.compressedSize + ", size="
+                        + ze.size + ", localHeader=" + ze.localHeaderRelOffset);
+            }
+
+            return false;
+        } else {
+            // If we're parsed the zip64 extended info header, we remove it from the extras
+            // so that applications that set their own extras will see the data they set.
+
+            // This is an unfortunate workaround needed due to a gap in the spec. The spec demands
+            // that extras are present in the "extensible" format, which means that each extra field
+            // must be prefixed with a header ID and a length. However, earlier versions of the spec
+            // made no mention of this, nor did any existing API enforce it. This means users could
+            // set "free form" extras without caring very much whether the implementation wanted to
+            // extend or add to them.
+
+            // The start of the extended info header.
+            final int extendedInfoHeaderStart = extendedInfoStart - 4;
+            // The total size of the extended info, including the header.
+            final int extendedInfoTotalSize = extendedInfoSize + 4;
+
+            final int extrasLen = ze.extra.length - extendedInfoTotalSize;
+            byte[] extrasWithoutZip64 = new byte[extrasLen];
+
+            System.arraycopy(ze.extra, 0, extrasWithoutZip64, 0, extendedInfoHeaderStart);
+            System.arraycopy(ze.extra, extendedInfoHeaderStart + extendedInfoTotalSize,
+                    extrasWithoutZip64, extendedInfoHeaderStart, (extrasLen - extendedInfoHeaderStart));
+
+            ze.extra = extrasWithoutZip64;
+            return true;
+        }
+    }
+
+    /**
+     * Appends a zip64 extended info record to the extras contained in {@code ze}. If {@code ze}
+     * contains no extras, a new extras array is created.
+     */
+    public static void insertZip64ExtendedInfoToExtras(ZipEntry ze) throws ZipException {
+        final byte[] output;
+        // We add 4 to ZIP64_EXTENDED_INFO_MIN_SIZE to account for the 2 byte header and length.
+        final int extendedInfoSize = ZIP64_EXTENDED_INFO_MIN_SIZE + 4;
+        if (ze.extra == null) {
+            output = new byte[extendedInfoSize];
+        } else {
+            // If the existing extras are already too big, we have no choice but to throw
+            // an error.
+            if (ze.extra.length + extendedInfoSize > 65535) {
+                throw new ZipException("No space in extras for zip64 extended entry info");
+            }
+
+            // We copy existing extras over and put the zip64 extended info at the beginning. This
+            // is to avoid breakages in the presence of "old style" extras which don't contain
+            // headers and lengths. The spec is again silent about these inconsistencies.
+            //
+            // This means that people that for ZipOutputStream users, the value ZipEntry.getExtra
+            // after an entry is written will be different from before. This shouldn't be an issue
+            // in practice.
+            output = new byte[ze.extra.length + ZIP64_EXTENDED_INFO_MIN_SIZE + 4];
+            System.arraycopy(ze.extra, 0, output,  ZIP64_EXTENDED_INFO_MIN_SIZE + 4, ze.extra.length);
+        }
+
+        ByteBuffer bb = ByteBuffer.wrap(output).order(ByteOrder.LITTLE_ENDIAN);
+        bb.putShort(ZIP64_EXTENDED_INFO_HEADER_ID);
+        bb.putShort((short) ZIP64_EXTENDED_INFO_MIN_SIZE);
+
+        if (ze.getMethod() == ZipEntry.STORED) {
+            bb.putLong(ze.size);
+            bb.putLong(ze.compressedSize);
+        } else {
+            // Store these fields in the data descriptor instead.
+            bb.putLong(0); // size.
+            bb.putLong(0); // compressed size.
+        }
+
+        // The offset is only relevant in the central directory entry, but we write it out here
+        // anyway, since we know what it is.
+        bb.putLong(ze.localHeaderRelOffset);
+        bb.putInt(0);  //  disk number
+
+        ze.extra = output;
+    }
+
+    /**
+     * Returns the size of the extended info record if {@code extras} contains a zip64 extended info
+     * record, {@code -1} otherwise. The buffer will be positioned at the start of the extended info
+     * record.
+     */
+    private static int getZip64ExtendedInfoSize(ByteBuffer extras) {
+        try {
+            while (extras.hasRemaining()) {
+                final int headerId = extras.getShort() & 0xffff;
+                final int length = extras.getShort() & 0xffff;
+                if (headerId == ZIP64_EXTENDED_INFO_HEADER_ID) {
+                    if (extras.remaining() >= length) {
+                        return length;
+                    } else {
+                        return -1;
+                    }
+                } else {
+                    extras.position(extras.position() + length);
+                }
+            }
+
+            return -1;
+        } catch (BufferUnderflowException bue) {
+            // We'll underflow if we have an incomplete header in our extras.
+            return -1;
+        } catch (IllegalArgumentException iae) {
+            // ByteBuffer.position() will throw if we have a truncated extra or
+            // an invalid length in the header.
+            return -1;
+        }
+    }
+
+    /**
+     * Copy the size, compressed size and local header offset fields from {@code ze} to
+     * inside {@code ze}'s extended info record. This is additional step is necessary when
+     * we could calculate the correct sizes only after writing out the entry. In this case,
+     * the local file header would not contain real sizes, and they would be present in the
+     * data descriptor and the central directory only.
+     */
+    public static void refreshZip64ExtendedInfo(ZipEntry ze) {
+        if (ze.extra == null || ze.extra.length < ZIP64_EXTENDED_INFO_MIN_SIZE) {
+            throw new IllegalStateException("Zip64 entry has no available extras: " + ze);
+        }
+
+
+        ByteBuffer buf = ByteBuffer.wrap(ze.extra).order(ByteOrder.LITTLE_ENDIAN);
+        if (getZip64ExtendedInfoSize(buf) == -1) {
+            throw new IllegalStateException(
+                    "Zip64 entry extras has no zip64 extended info record: " + ze);
+        }
+
+        buf.putLong(ze.size);
+        buf.putLong(ze.compressedSize);
+        buf.putLong(ze.localHeaderRelOffset);
+        buf.putInt(0); // disk number.
+    }
+
+    public static void writeZip64EocdRecordAndLocator(ByteArrayOutputStream baos,
+            long numEntries, long offset, long cDirSize) throws IOException {
+        // Step 1: Write out the zip64 EOCD record.
+        writeLongAsUint32(baos, ZIP64_EOCD_RECORD_SIGNATURE);
+        // The size of the zip64 eocd record. This is the effective size + the
+        // size of the "version made by" (2 bytes) and the "version needed to extract" (2 bytes)
+        // fields.
+        writeLongAsUint64(baos, ZIP64_EOCD_RECORD_EFFECTIVE_SIZE + 4);
+        // TODO: What values should we put here ? The pre-zip64 values we've chosen don't
+        // seem to make much sense either.
+        writeIntAsUint16(baos, 20);
+        writeIntAsUint16(baos, 20);
+        writeLongAsUint32(baos, 0L); // number of disk
+        writeLongAsUint32(baos, 0L); // number of disk with start of central dir.
+        writeLongAsUint64(baos, numEntries); // number of entries in this disk.
+        writeLongAsUint64(baos, numEntries); // number of entries in total.
+        writeLongAsUint64(baos, cDirSize); // size of the central directory.
+        writeLongAsUint64(baos, offset); // offset of the central directory wrt. this file.
+
+        // Step 2: Write out the zip64 EOCD record locator.
+        writeLongAsUint32(baos, ZIP64_LOCATOR_SIGNATURE);
+        writeLongAsUint32(baos, 0); // number of disk with start of central dir.
+        writeLongAsUint64(baos, offset + cDirSize); // offset of the eocd record wrt. this file.
+        writeLongAsUint32(baos, 1); // total number of disks.
+    }
+}
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index 217cc3c..26f6863 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -52,7 +52,6 @@
 
     byte[] extra;
 
-    int nameLength = -1;
     long localHeaderRelOffset = -1;
 
     long dataOffset = -1;
@@ -69,7 +68,7 @@
 
     ZipEntry(String name, String comment, long crc, long compressedSize,
             long size, int compressionMethod, int time, int modDate, byte[] extra,
-            int nameLength, long localHeaderRelOffset, long dataOffset) {
+            long localHeaderRelOffset, long dataOffset) {
         this.name = name;
         this.comment = comment;
         this.crc = crc;
@@ -79,7 +78,6 @@
         this.time = time;
         this.modDate = modDate;
         this.extra = extra;
-        this.nameLength = nameLength;
         this.localHeaderRelOffset = localHeaderRelOffset;
         this.dataOffset = dataOffset;
     }
@@ -149,6 +147,11 @@
     /**
      * Gets the name of this {@code ZipEntry}.
      *
+     * <p><em>Security note:</em> Entry names can represent relative paths. {@code foo/../bar} or
+     * {@code ../bar/baz}, for example. If the entry name is being used to construct a filename
+     * or as a path component, it must be validated or sanitized to ensure that files are not
+     * written outside of the intended destination directory.
+     *
      * @return the entry name.
      */
     public String getName() {
@@ -265,17 +268,15 @@
     /**
      * Sets the uncompressed size of this {@code ZipEntry}.
      *
-     * @param value
-     *            the uncompressed size for this entry.
-     * @throws IllegalArgumentException
-     *             if {@code value} < 0 or {@code value} > 0xFFFFFFFFL.
+     * @param value the uncompressed size for this entry.
+     * @throws IllegalArgumentException if {@code value < 0}.
      */
     public void setSize(long value) {
-        if (value >= 0 && value <= 0xFFFFFFFFL) {
-            size = value;
-        } else {
+        if (value < 0) {
             throw new IllegalArgumentException("Bad size: " + value);
         }
+
+        size = value;
     }
 
     /**
@@ -340,7 +341,6 @@
         compressionMethod = ze.compressionMethod;
         modDate = ze.modDate;
         extra = ze.extra;
-        nameLength = ze.nameLength;
         localHeaderRelOffset = ze.localHeaderRelOffset;
         dataOffset = ze.dataOffset;
     }
@@ -378,7 +378,7 @@
      * On exit, "in" will be positioned at the start of the next entry
      * in the Central Directory.
      */
-    ZipEntry(byte[] cdeHdrBuf, InputStream cdStream, Charset defaultCharset) throws IOException {
+    ZipEntry(byte[] cdeHdrBuf, InputStream cdStream, Charset defaultCharset, boolean isZip64) throws IOException {
         Streams.readFully(cdStream, cdeHdrBuf, 0, cdeHdrBuf.length);
 
         BufferIterator it = HeapBufferIterator.iterator(cdeHdrBuf, 0, cdeHdrBuf.length,
@@ -412,7 +412,7 @@
         compressedSize = ((long) it.readInt()) & 0xffffffffL;
         size = ((long) it.readInt()) & 0xffffffffL;
 
-        nameLength = it.readShort() & 0xffff;
+        int nameLength = it.readShort() & 0xffff;
         int extraLength = it.readShort() & 0xffff;
         int commentByteCount = it.readShort() & 0xffff;
 
@@ -437,6 +437,10 @@
             Streams.readFully(cdStream, commentBytes, 0, commentByteCount);
             comment = new String(commentBytes, 0, commentBytes.length, charset);
         }
+
+        if (isZip64) {
+            Zip64.parseZip64ExtendedInfo(this, true /* from central directory */);
+        }
     }
 
     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 b44156e..307e7fe 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -107,6 +107,18 @@
 
     private final CloseGuard guard = CloseGuard.get();
 
+    static class EocdRecord {
+        final long numEntries;
+        final long centralDirOffset;
+        final int commentLength;
+
+        EocdRecord(long numEntries, long centralDirOffset, int commentLength) {
+            this.numEntries = numEntries;
+            this.centralDirOffset = centralDirOffset;
+            this.commentLength = commentLength;
+        }
+    }
+
     /**
      * Constructs a new {@code ZipFile} allowing read access to the contents of the given file.
      *
@@ -390,9 +402,11 @@
             stopOffset = 0;
         }
 
+        long eocdOffset;
         while (true) {
             raf.seek(scanOffset);
             if (Integer.reverseBytes(raf.readInt()) == ENDSIG) {
+                eocdOffset = scanOffset;
                 break;
             }
 
@@ -402,41 +416,35 @@
             }
         }
 
-        // Read the End Of Central Directory. ENDHDR includes the signature bytes,
-        // which we've already read.
-        byte[] eocd = new byte[ENDHDR - 4];
-        raf.readFully(eocd);
+        final long zip64EocdRecordOffset = Zip64.parseZip64EocdRecordLocator(raf, eocdOffset);
 
-        // Pull out the information we need.
-        BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN);
-        int diskNumber = it.readShort() & 0xffff;
-        int diskWithCentralDir = it.readShort() & 0xffff;
-        int numEntries = it.readShort() & 0xffff;
-        int totalNumEntries = it.readShort() & 0xffff;
-        it.skip(4); // Ignore centralDirSize.
-        long centralDirOffset = ((long) it.readInt()) & 0xffffffffL;
-        int commentLength = it.readShort() & 0xffff;
-
-        if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
-            throw new ZipException("Spanned archives not supported");
-        }
-
-        if (commentLength > 0) {
-            byte[] commentBytes = new byte[commentLength];
+        // Seek back past the eocd signature so that we can continue with our search.
+        // Note that we add 4 bytes to the offset to skip past the signature.
+        EocdRecord record = parseEocdRecord(raf, eocdOffset + 4, (zip64EocdRecordOffset != -1) /* isZip64 */);
+        // Read the comment now to avoid an additional seek. We also know the commentLength
+        // won't change because that information isn't present in the zip64 eocd record.
+        if (record.commentLength > 0) {
+            byte[] commentBytes = new byte[record.commentLength];
             raf.readFully(commentBytes);
             comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
         }
 
+        // We have a zip64 eocd record : use that for getting the information we need.
+        if (zip64EocdRecordOffset != -1) {
+            record = Zip64.parseZip64EocdRecord(raf, zip64EocdRecordOffset, record.commentLength);
+        }
+
         // Seek to the first CDE and read all entries.
         // We have to do this now (from the constructor) rather than lazily because the
         // public API doesn't allow us to throw IOException except from the constructor
         // or from getInputStream.
-        RAFStream rafStream = new RAFStream(raf, centralDirOffset);
+        RAFStream rafStream = new RAFStream(raf, record.centralDirOffset);
         BufferedInputStream bufferedStream = new BufferedInputStream(rafStream, 4096);
         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, StandardCharsets.UTF_8);
-            if (newEntry.localHeaderRelOffset >= centralDirOffset) {
+        for (long i = 0; i < record.numEntries; ++i) {
+            ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream, StandardCharsets.UTF_8,
+                    (zip64EocdRecordOffset != -1) /* isZip64 */);
+            if (newEntry.localHeaderRelOffset >= record.centralDirOffset) {
                 throw new ZipException("Local file header offset is after central directory");
             }
             String entryName = newEntry.getName();
@@ -446,6 +454,45 @@
         }
     }
 
+    private static EocdRecord parseEocdRecord(RandomAccessFile raf, long offset, boolean isZip64) throws IOException {
+        raf.seek(offset);
+
+        // Read the End Of Central Directory. ENDHDR includes the signature bytes,
+        // which we've already read.
+        byte[] eocd = new byte[ENDHDR - 4];
+        raf.readFully(eocd);
+
+        BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN);
+        final long numEntries;
+        final long centralDirOffset;
+        if (isZip64) {
+            numEntries = -1;
+            centralDirOffset = -1;
+
+            // If we have a zip64 end of central directory record, we skip through the regular
+            // end of central directory record and use the information from the zip64 eocd record.
+            // We're still forced to read the comment length (below) since it isn't present in the
+            // zip64 eocd record.
+            it.skip(16);
+        } else {
+            // If we don't have a zip64 eocd record, we read values from the "regular"
+            // eocd record.
+            int diskNumber = it.readShort() & 0xffff;
+            int diskWithCentralDir = it.readShort() & 0xffff;
+            numEntries = it.readShort() & 0xffff;
+            int totalNumEntries = it.readShort() & 0xffff;
+            it.skip(4); // Ignore centralDirSize.
+
+            centralDirOffset = ((long) it.readInt()) & 0xffffffffL;
+            if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
+                throw new ZipException("Spanned archives not supported");
+            }
+        }
+
+        final int commentLength = it.readShort() & 0xffff;
+        return new EocdRecord(numEntries, centralDirOffset, commentLength);
+    }
+
     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);
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index 4c0034e..f3ca74e 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -81,7 +81,9 @@
 
     private ZipEntry currentEntry;
 
-    private final byte[] hdrBuf = new byte[LOCHDR - LOCVER];
+    private boolean currentEntryIsZip64;
+
+    private final byte[] hdrBuf = new byte[LOCHDR - LOCVER + 8];
 
     private final CRC32 crc = new CRC32();
 
@@ -159,7 +161,7 @@
         }
 
         try {
-            readAndVerifyDataDescriptor(inB, out);
+            readAndVerifyDataDescriptor(inB, out, currentEntryIsZip64);
         } catch (Exception e) {
             if (failure == null) { // otherwise we're already going to throw
                 failure = e;
@@ -183,16 +185,31 @@
         }
     }
 
-    private void readAndVerifyDataDescriptor(int inB, int out) throws IOException {
+    private void readAndVerifyDataDescriptor(long inB, long out, boolean isZip64) throws IOException {
         if (hasDD) {
-            Streams.readFully(in, hdrBuf, 0, EXTHDR);
+            if (isZip64) {
+                // 8 additional bytes since the compressed / uncompressed size fields
+                // in the extended header are 8 bytes each, instead of 4 bytes each.
+                Streams.readFully(in, hdrBuf, 0, EXTHDR + 8);
+            } else {
+                Streams.readFully(in, hdrBuf, 0, EXTHDR);
+            }
+
             int sig = Memory.peekInt(hdrBuf, 0, ByteOrder.LITTLE_ENDIAN);
             if (sig != (int) EXTSIG) {
                 throw new ZipException(String.format("unknown format (EXTSIG=%x)", sig));
             }
             currentEntry.crc = ((long) Memory.peekInt(hdrBuf, EXTCRC, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
-            currentEntry.compressedSize = ((long) Memory.peekInt(hdrBuf, EXTSIZ, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
-            currentEntry.size = ((long) Memory.peekInt(hdrBuf, EXTLEN, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
+
+            if (isZip64) {
+                currentEntry.compressedSize = Memory.peekLong(hdrBuf, EXTSIZ, ByteOrder.LITTLE_ENDIAN);
+                // Note that we apply an adjustment of 4 bytes to the offset of EXTLEN to account
+                // for the 8 byte size for zip64.
+                currentEntry.size = Memory.peekLong(hdrBuf, EXTLEN + 4, ByteOrder.LITTLE_ENDIAN);
+            } else {
+                currentEntry.compressedSize = ((long) Memory.peekInt(hdrBuf, EXTSIZ, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
+                currentEntry.size = ((long) Memory.peekInt(hdrBuf, EXTLEN, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
+            }
         }
         if (currentEntry.crc != crc.getValue()) {
             throw new ZipException("CRC mismatch");
@@ -266,7 +283,11 @@
             byte[] extraData = new byte[extraLength];
             Streams.readFully(in, extraData, 0, extraLength);
             currentEntry.setExtra(extraData);
+            currentEntryIsZip64 = Zip64.parseZip64ExtendedInfo(currentEntry, false /* from central directory */);
+        } else {
+            currentEntryIsZip64 = false;
         }
+
         return currentEntry;
     }
 
diff --git a/luni/src/main/java/java/util/zip/ZipOutputStream.java b/luni/src/main/java/java/util/zip/ZipOutputStream.java
index 8278355..7748cfd 100644
--- a/luni/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipOutputStream.java
@@ -23,6 +23,8 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.HashSet;
+
+import libcore.util.CountingOutputStream;
 import libcore.util.EmptyArray;
 
 /**
@@ -85,7 +87,7 @@
 
     private final CRC32 crc = new CRC32();
 
-    private int offset = 0, curOffset = 0;
+    private long offset = 0;
 
     /** The charset-encoded name for the current entry. */
     private byte[] nameBytes;
@@ -93,6 +95,31 @@
     /** The charset-encoded comment for the current entry. */
     private byte[] entryCommentBytes;
 
+    private static final byte[] ZIP64_PLACEHOLDER_BYTES =
+            new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+
+    /**
+     * Whether this zip file needs a Zip64 EOCD record / zip64 EOCD record locator. This
+     * will be true if we wrote an entry whose size or compressed size was too large for
+     * the standard zip format or if we exceeded the maximum number of entries allowed
+     * in the standard format.
+     */
+    private boolean archiveNeedsZip64EocdRecord;
+
+    /**
+     * Whether the current entry being processed needs a zip64 extended info record. This
+     * will be true if the entry is too large for the standard zip format or if the offset
+     * to the start of the current entry header is greater than 0xFFFFFFFF.
+     */
+    private boolean currentEntryNeedsZip64;
+
+    /**
+     * Whether we force all entries in this archive to have a zip64 extended info record.
+     * This of course implies that the {@code currentEntryNeedsZip64} and
+     * {@code archiveNeedsZip64EocdRecord} are always {@code true}.
+     */
+    private final boolean forceZip64;
+
     /**
      * Constructs a new {@code ZipOutputStream} that writes a zip file to the given
      * {@code OutputStream}.
@@ -100,7 +127,15 @@
      * <p>UTF-8 will be used to encode the file comment, entry names and comments.
      */
     public ZipOutputStream(OutputStream os) {
-        super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+        this(os, false /* forceZip64 */);
+    }
+
+    /**
+     * @hide for testing only.
+     */
+    public ZipOutputStream(OutputStream os, boolean forceZip64) {
+        super(new CountingOutputStream(os), new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+        this.forceZip64 = forceZip64;
     }
 
     /**
@@ -146,15 +181,30 @@
                 throw new ZipException("Size mismatch");
             }
         }
-        curOffset = LOCHDR;
+
+        long curOffset = LOCHDR;
 
         // Write the DataDescriptor
         if (currentEntry.getMethod() != STORED) {
             curOffset += EXTHDR;
-            writeLong(out, EXTSIG);
-            writeLong(out, currentEntry.crc = crc.getValue());
-            writeLong(out, currentEntry.compressedSize = def.getTotalOut());
-            writeLong(out, currentEntry.size = def.getTotalIn());
+
+            // Data descriptor signature and CRC are 4 bytes each for both zip and zip64.
+            writeLongAsUint32(out, EXTSIG);
+            writeLongAsUint32(out, currentEntry.crc = crc.getValue());
+
+            currentEntry.compressedSize = def.getBytesWritten();
+            currentEntry.size = def.getBytesRead();
+
+            if (currentEntryNeedsZip64) {
+                // We need an additional 8 bytes to store 8 byte compressed / uncompressed
+                // sizes.
+                curOffset += 8;
+                writeLongAsUint64(out, currentEntry.compressedSize);
+                writeLongAsUint64(out, currentEntry.size);
+            } else {
+                writeLongAsUint32(out, currentEntry.compressedSize);
+                writeLongAsUint32(out, currentEntry.size);
+            }
         }
         // Update the CentralDirectory
         // http://www.pkware.com/documents/casestudies/APPNOTE.TXT
@@ -163,33 +213,54 @@
         // Some tools insist that the central directory have the UTF-8 flag.
         // http://code.google.com/p/android/issues/detail?id=20214
         flags |= ZipFile.GPBF_UTF8_FLAG;
-        writeLong(cDir, CENSIG);
-        writeShort(cDir, ZIP_VERSION_2_0); // Version this file was made by.
-        writeShort(cDir, ZIP_VERSION_2_0); // Minimum version needed to extract.
-        writeShort(cDir, flags);
-        writeShort(cDir, currentEntry.getMethod());
-        writeShort(cDir, currentEntry.time);
-        writeShort(cDir, currentEntry.modDate);
-        writeLong(cDir, crc.getValue());
+        writeLongAsUint32(cDir, CENSIG);
+        writeIntAsUint16(cDir, ZIP_VERSION_2_0); // Version this file was made by.
+        writeIntAsUint16(cDir, ZIP_VERSION_2_0); // Minimum version needed to extract.
+        writeIntAsUint16(cDir, flags);
+        writeIntAsUint16(cDir, currentEntry.getMethod());
+        writeIntAsUint16(cDir, currentEntry.time);
+        writeIntAsUint16(cDir, currentEntry.modDate);
+        writeLongAsUint32(cDir, crc.getValue());
+
         if (currentEntry.getMethod() == DEFLATED) {
-            curOffset += writeLong(cDir, def.getTotalOut());
-            writeLong(cDir, def.getTotalIn());
+            currentEntry.setCompressedSize(def.getBytesWritten());
+            currentEntry.setSize(def.getBytesRead());
+            curOffset += currentEntry.getCompressedSize();
         } else {
-            curOffset += writeLong(cDir, crc.tbytes);
-            writeLong(cDir, crc.tbytes);
-        }
-        curOffset += writeShort(cDir, nameBytes.length);
-        if (currentEntry.extra != null) {
-            curOffset += writeShort(cDir, currentEntry.extra.length);
-        } else {
-            writeShort(cDir, 0);
+            currentEntry.setCompressedSize(crc.tbytes);
+            currentEntry.setSize(crc.tbytes);
+            curOffset += currentEntry.getSize();
         }
 
-        writeShort(cDir, entryCommentBytes.length); // Comment length.
-        writeShort(cDir, 0); // Disk Start
-        writeShort(cDir, 0); // Internal File Attributes
-        writeLong(cDir, 0); // External File Attributes
-        writeLong(cDir, offset);
+        if (currentEntryNeedsZip64) {
+            // Refresh the extended info with the compressed size / size before
+            // writing it to the central directory.
+            Zip64.refreshZip64ExtendedInfo(currentEntry);
+
+            // NOTE: We would've written out the zip64 extended info locator to the entry
+            // extras while constructing the local file header. There's no need to do it again
+            // here. If we do, there will be a size mismatch since we're calculating offsets
+            // based on the *current* size of the extra data and not based on the size
+            // at the point of writing the LFH.
+            writeLongAsUint32(cDir, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE);
+            writeLongAsUint32(cDir, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE);
+        } else {
+            writeLongAsUint32(cDir, currentEntry.getCompressedSize());
+            writeLongAsUint32(cDir, currentEntry.getSize());
+        }
+
+        curOffset += writeIntAsUint16(cDir, nameBytes.length);
+        if (currentEntry.extra != null) {
+            curOffset += writeIntAsUint16(cDir, currentEntry.extra.length);
+        } else {
+            writeIntAsUint16(cDir, 0);
+        }
+
+        writeIntAsUint16(cDir, entryCommentBytes.length); // Comment length.
+        writeIntAsUint16(cDir, 0); // Disk Start
+        writeIntAsUint16(cDir, 0); // Internal File Attributes
+        writeLongAsUint32(cDir, 0); // External File Attributes
+        writeLongAsUint32(cDir, offset);
         cDir.write(nameBytes);
         nameBytes = null;
         if (currentEntry.extra != null) {
@@ -228,16 +299,32 @@
         if (currentEntry != null) {
             closeEntry();
         }
-        int cdirSize = cDir.size();
+
+        int cdirEntriesSize = cDir.size();
+        if (archiveNeedsZip64EocdRecord) {
+            Zip64.writeZip64EocdRecordAndLocator(cDir, entries.size(), offset, cdirEntriesSize);
+        }
+
         // Write Central Dir End
-        writeLong(cDir, ENDSIG);
-        writeShort(cDir, 0); // Disk Number
-        writeShort(cDir, 0); // Start Disk
-        writeShort(cDir, entries.size()); // Number of entries
-        writeShort(cDir, entries.size()); // Number of entries
-        writeLong(cDir, cdirSize); // Size of central dir
-        writeLong(cDir, offset); // Offset of central dir
-        writeShort(cDir, commentBytes.length);
+        writeLongAsUint32(cDir, ENDSIG);
+        writeIntAsUint16(cDir, 0); // Disk Number
+        writeIntAsUint16(cDir, 0); // Start Disk
+
+        // Instead of trying to figure out *why* this archive needed a zip64 eocd record,
+        // just delegate all these values to the zip64 eocd record.
+        if (archiveNeedsZip64EocdRecord) {
+            writeIntAsUint16(cDir, 0xFFFF); // Number of entries
+            writeIntAsUint16(cDir, 0xFFFF); // Number of entries
+            writeLongAsUint32(cDir, 0xFFFFFFFF); // Size of central dir
+            writeLongAsUint32(cDir, 0xFFFFFFFF); // Offset of central dir;
+        } else {
+            writeIntAsUint16(cDir, entries.size()); // Number of entries
+            writeIntAsUint16(cDir, entries.size()); // Number of entries
+            writeLongAsUint32(cDir, cdirEntriesSize); // Size of central dir
+            writeLongAsUint32(cDir, offset); // Offset of central dir
+        }
+
+        writeIntAsUint16(cDir, commentBytes.length);
         if (commentBytes.length > 0) {
             cDir.write(commentBytes);
         }
@@ -288,14 +375,8 @@
         }
 
         checkOpen();
+        checkAndSetZip64Requirements(ze);
 
-        if (entries.contains(ze.name)) {
-            throw new ZipException("Entry already exists: " + ze.name);
-        }
-        if (entries.size() == 64*1024-1) {
-            // TODO: support Zip64.
-            throw new ZipException("Too many entries for the zip file format's 16-bit entry count");
-        }
         nameBytes = ze.name.getBytes(StandardCharsets.UTF_8);
         checkSizeIsWithinShort("Name", nameBytes);
         entryCommentBytes = EmptyArray.BYTE;
@@ -310,6 +391,7 @@
         ze.setMethod(method);
 
         currentEntry = ze;
+        currentEntry.localHeaderRelOffset = offset;
         entries.add(currentEntry.name);
 
         // Local file header.
@@ -318,30 +400,48 @@
         // Java always outputs UTF-8 filenames. (Before Java 7, the RI didn't set this flag and used
         // modified UTF-8. From Java 7, when using UTF_8 it sets this flag and uses normal UTF-8.)
         flags |= ZipFile.GPBF_UTF8_FLAG;
-        writeLong(out, LOCSIG); // Entry header
-        writeShort(out, ZIP_VERSION_2_0); // Minimum version needed to extract.
-        writeShort(out, flags);
-        writeShort(out, method);
+        writeLongAsUint32(out, LOCSIG); // Entry header
+        writeIntAsUint16(out, ZIP_VERSION_2_0); // Minimum version needed to extract.
+        writeIntAsUint16(out, flags);
+        writeIntAsUint16(out, method);
         if (currentEntry.getTime() == -1) {
             currentEntry.setTime(System.currentTimeMillis());
         }
-        writeShort(out, currentEntry.time);
-        writeShort(out, currentEntry.modDate);
+        writeIntAsUint16(out, currentEntry.time);
+        writeIntAsUint16(out, currentEntry.modDate);
 
         if (method == STORED) {
-            writeLong(out, currentEntry.crc);
-            writeLong(out, currentEntry.size);
-            writeLong(out, currentEntry.size);
+            writeLongAsUint32(out, currentEntry.crc);
+
+            if (currentEntryNeedsZip64) {
+                // NOTE: According to the spec, we're allowed to use these fields under zip64
+                // as long as the sizes are <= 4G (and omit writing the zip64 extended information header).
+                //
+                // For simplicity, we write the zip64 extended info here even if we only need it
+                // in the central directory (i.e, the case where we're turning on zip64 because the
+                // offset to this entries LFH is > 0xFFFFFFFF).
+                out.write(ZIP64_PLACEHOLDER_BYTES);  // compressed size
+                out.write(ZIP64_PLACEHOLDER_BYTES);  // uncompressed size
+            } else {
+                writeLongAsUint32(out, currentEntry.size);
+                writeLongAsUint32(out, currentEntry.size);
+            }
         } else {
-            writeLong(out, 0);
-            writeLong(out, 0);
-            writeLong(out, 0);
+            writeLongAsUint32(out, 0);
+            writeLongAsUint32(out, 0);
+            writeLongAsUint32(out, 0);
         }
-        writeShort(out, nameBytes.length);
+
+        writeIntAsUint16(out, nameBytes.length);
+
+        if (currentEntryNeedsZip64) {
+            Zip64.insertZip64ExtendedInfoToExtras(currentEntry);
+        }
+
         if (currentEntry.extra != null) {
-            writeShort(out, currentEntry.extra.length);
+            writeIntAsUint16(out, currentEntry.extra.length);
         } else {
-            writeShort(out, 0);
+            writeIntAsUint16(out, 0);
         }
         out.write(nameBytes);
         if (currentEntry.extra != null) {
@@ -349,6 +449,37 @@
         }
     }
 
+    private void checkAndSetZip64Requirements(ZipEntry entry) {
+        final long totalBytesWritten = getBytesWritten();
+        final long entriesWritten = entries.size();
+
+        currentEntryNeedsZip64 = false;
+        if (forceZip64) {
+            currentEntryNeedsZip64 = true;
+            archiveNeedsZip64EocdRecord = true;
+            return;
+        }
+
+        // In this particular case, we'll write a zip64 eocd record locator and a zip64 eocd
+        // record but we won't actually need zip64 extended info records for any of the individual
+        // entries (unless they trigger the checks below).
+        if (entriesWritten == 64*1024 - 1) {
+            archiveNeedsZip64EocdRecord = true;
+        }
+
+        // Check whether we'll need to write out a zip64 extended info record in both the local file header
+        // and the central directory. In addition, we will need a zip64 eocd record locator
+        // and record to mark this archive as zip64.
+        //
+        // TODO: This is an imprecise check. When method != STORED it's possible that the compressed
+        // size will be (slightly) larger than the actual size. How can we improve this ?
+        if (totalBytesWritten > Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE ||
+                (entry.getSize() > Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE)) {
+            currentEntryNeedsZip64 = true;
+            archiveNeedsZip64EocdRecord = true;
+        }
+    }
+
     /**
      * Sets the comment associated with the file being written. See {@link ZipFile#getComment}.
      * @throws IllegalArgumentException if the comment is >= 64 Ki encoded bytes.
@@ -386,7 +517,7 @@
         defaultCompressionMethod = method;
     }
 
-    private long writeLong(OutputStream os, long i) throws IOException {
+    static long writeLongAsUint32(OutputStream os, long i) throws IOException {
         // Write out the long value as an unsigned int
         os.write((int) (i & 0xFF));
         os.write((int) (i >> 8) & 0xFF);
@@ -395,7 +526,23 @@
         return i;
     }
 
-    private int writeShort(OutputStream os, int i) throws IOException {
+    static long writeLongAsUint64(OutputStream os, long i) throws IOException {
+        int i1 = (int) i;
+        os.write(i1 & 0xFF);
+        os.write((i1 >> 8) & 0xFF);
+        os.write((i1 >> 16) & 0xFF);
+        os.write((i1 >> 24) & 0xFF);
+
+        int i2 = (int) (i >> 32);
+        os.write(i2 & 0xFF);
+        os.write((i2 >> 8) & 0xFF);
+        os.write((i2 >> 16) & 0xFF);
+        os.write((i2 >> 24) & 0xFF);
+
+        return i;
+    }
+
+    static int writeIntAsUint16(OutputStream os, int i) throws IOException {
         os.write(i & 0xFF);
         os.write((i >> 8) & 0xFF);
         return i;
@@ -414,6 +561,13 @@
             throw new ZipException("No active entry");
         }
 
+        final long totalBytes = crc.tbytes + byteCount;
+        if ((totalBytes > Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) && !currentEntryNeedsZip64) {
+            throw new IOException("Zip entry size (" + totalBytes +
+                    " bytes) cannot be represented in the zip format (needs Zip64)." +
+                    " Set the entry length using ZipEntry#setLength to use Zip64 where necessary.");
+        }
+
         if (currentEntry.getMethod() == STORED) {
             out.write(buffer, offset, byteCount);
         } else {
@@ -434,4 +588,11 @@
                                                " bytes");
         }
     }
+
+    private long getBytesWritten() {
+        // This cast is somewhat messy but less error prone than keeping an
+        // CountingOutputStream reference around in addition to the FilterOutputStream's
+        // out.
+        return ((CountingOutputStream) out).getCount();
+    }
 }
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index 2e3b341..66d03ad 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -366,8 +366,10 @@
 
     /**
      * Convenience call when the Key is not available.
+     *
+     * @hide
      */
-    private CipherSpi getSpi() {
+    public CipherSpi getSpi() {
         return getSpi(null);
     }
 
@@ -419,7 +421,7 @@
             if (service == null) {
                 return null;
             }
-            return tryTransformWithProvider(key, transformParts, type, service);
+            return tryTransformWithProvider(null, transformParts, type, service);
         }
         ArrayList<Provider.Service> services = ENGINE.getServices(transform);
         if (services == null) {
@@ -1005,8 +1007,7 @@
      *            the offset in the input to start.
      * @param inputLen
      *            the length of the input to transform.
-     * @return the transformed bytes in a new buffer, or {@code null} if the
-     *         input has zero length.
+     * @return the transformed bytes in a new buffer, or {@code null} if {@code inputLen} is zero.
      * @throws IllegalStateException
      *             if this cipher instance is not initialized for encryption or
      *             decryption.
@@ -1023,7 +1024,7 @@
             throw new IllegalArgumentException("input == null");
         }
         checkInputOffsetAndCount(input.length, inputOffset, inputLen);
-        if (input.length == 0) {
+        if (inputLen == 0) {
             return null;
         }
         return getSpi().engineUpdate(input, inputOffset, inputLen);
diff --git a/luni/src/main/java/javax/crypto/ExemptionMechanism.java b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
index c2d42e6..eede649 100644
--- a/luni/src/main/java/javax/crypto/ExemptionMechanism.java
+++ b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
@@ -361,9 +361,6 @@
         return len;
     }
 
-    /**
-     * Override to clear any key state in the instance.
-     */
     @Override protected void finalize() {
         try {
             super.finalize();
diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java
index abcfd0e..d27aa2e 100644
--- a/luni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/luni/src/main/java/javax/crypto/KeyAgreement.java
@@ -195,7 +195,7 @@
             if (service == null) {
                 return null;
             }
-            return tryAlgorithmWithProvider(key, service);
+            return tryAlgorithmWithProvider(null, service);
         }
         ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
         if (services == null) {
@@ -252,8 +252,10 @@
 
     /**
      * Convenience call when the Key is not available.
+     *
+     * @hide
      */
-    private KeyAgreementSpi getSpi() {
+    public KeyAgreementSpi getSpi() {
         return getSpi(null);
     }
 
diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java
index 5a73dc5..536f0c5 100644
--- a/luni/src/main/java/javax/crypto/Mac.java
+++ b/luni/src/main/java/javax/crypto/Mac.java
@@ -199,7 +199,7 @@
             if (service == null) {
                 return null;
             }
-            return tryAlgorithmWithProvider(key, service);
+            return tryAlgorithmWithProvider(null, service);
         }
         ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
         if (services == null) {
@@ -266,8 +266,10 @@
 
     /**
      * Convenience call when the Key is not available.
+     *
+     * @hide
      */
-    private MacSpi getSpi() {
+    public MacSpi getSpi() {
         return getSpi(null);
     }
 
diff --git a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
deleted file mode 100644
index fd84c3e..0000000
--- a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
+++ /dev/null
@@ -1,169 +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 javax.net.ssl;
-
-import java.net.InetAddress;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import javax.security.auth.x500.X500Principal;
-
-/**
- * A HostnameVerifier consistent with <a
- * href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>.
- *
- * @hide accessible via HttpsURLConnection.getDefaultHostnameVerifier()
- */
-public final class DefaultHostnameVerifier implements HostnameVerifier {
-    private static final int ALT_DNS_NAME = 2;
-    private static final int ALT_IPA_NAME = 7;
-
-    public final boolean verify(String host, SSLSession session) {
-        try {
-            Certificate[] certificates = session.getPeerCertificates();
-            return verify(host, (X509Certificate) certificates[0]);
-        } catch (SSLException e) {
-            return false;
-        }
-    }
-
-    public boolean verify(String host, X509Certificate certificate) {
-        return InetAddress.isNumeric(host)
-                ? verifyIpAddress(host, certificate)
-                : verifyHostName(host, certificate);
-    }
-
-    /**
-     * Returns true if {@code certificate} matches {@code ipAddress}.
-     */
-    private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) {
-        for (String altName : getSubjectAltNames(certificate, ALT_IPA_NAME)) {
-            if (ipAddress.equalsIgnoreCase(altName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if {@code certificate} matches {@code hostName}.
-     */
-    private boolean verifyHostName(String hostName, X509Certificate certificate) {
-        hostName = hostName.toLowerCase(Locale.US);
-        boolean hasDns = false;
-        for (String altName : getSubjectAltNames(certificate, ALT_DNS_NAME)) {
-            hasDns = true;
-            if (verifyHostName(hostName, altName)) {
-                return true;
-            }
-        }
-
-        if (!hasDns) {
-            X500Principal principal = certificate.getSubjectX500Principal();
-            // RFC 2818 advises using the most specific name for matching.
-            String cn = new DistinguishedNameParser(principal).findMostSpecific("cn");
-            if (cn != null) {
-                return verifyHostName(hostName, cn);
-            }
-        }
-
-        return false;
-    }
-
-    private List<String> getSubjectAltNames(X509Certificate certificate, int type) {
-        List<String> result = new ArrayList<String>();
-        try {
-            Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
-            if (subjectAltNames == null) {
-                return Collections.emptyList();
-            }
-            for (Object subjectAltName : subjectAltNames) {
-                List<?> entry = (List<?>) subjectAltName;
-                if (entry == null || entry.size() < 2) {
-                    continue;
-                }
-                Integer altNameType = (Integer) entry.get(0);
-                if (altNameType == null) {
-                    continue;
-                }
-                if (altNameType == type) {
-                    String altName = (String) entry.get(1);
-                    if (altName != null) {
-                        result.add(altName);
-                    }
-                }
-            }
-            return result;
-        } catch (CertificateParsingException e) {
-            return Collections.emptyList();
-        }
-    }
-
-    /**
-     * Returns true if {@code hostName} matches the name or pattern {@code cn}.
-     *
-     * @param hostName lowercase host name.
-     * @param cn certificate host name. May include wildcards like
-     *     {@code *.android.com}.
-     */
-    public boolean verifyHostName(String hostName, String cn) {
-        if (hostName == null || hostName.isEmpty() || cn == null || cn.isEmpty()) {
-            return false;
-        }
-
-        cn = cn.toLowerCase(Locale.US);
-
-        if (!cn.contains("*")) {
-            return hostName.equals(cn);
-        }
-
-        if (cn.startsWith("*.") && hostName.equals(cn.substring(2))) {
-            return true; // "*.foo.com" matches "foo.com"
-        }
-
-        int asterisk = cn.indexOf('*');
-        int dot = cn.indexOf('.');
-        if (asterisk > dot) {
-            return false; // malformed; wildcard must be in the first part of the cn
-        }
-
-        if (!hostName.regionMatches(0, cn, 0, asterisk)) {
-            return false; // prefix before '*' doesn't match
-        }
-
-        int suffixLength = cn.length() - (asterisk + 1);
-        int suffixStart = hostName.length() - suffixLength;
-        if (hostName.indexOf('.', asterisk) < suffixStart) {
-            // TODO: remove workaround for *.clients.google.com http://b/5426333
-            if (!hostName.endsWith(".clients.google.com")) {
-                return false; // wildcard '*' can't match a '.'
-            }
-        }
-
-        if (!hostName.regionMatches(suffixStart, cn, asterisk + 1, suffixLength)) {
-            return false; // suffix after '*' doesn't match
-        }
-
-        return true;
-    }
-}
diff --git a/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java
index ab86a9b..1bd48fd 100644
--- a/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java
+++ b/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -109,7 +109,16 @@
      * it.
      */
     private static class NoPreloadHolder {
-        public static HostnameVerifier defaultHostnameVerifier = new DefaultHostnameVerifier();
+        public static HostnameVerifier defaultHostnameVerifier;
+        static {
+            try {
+                defaultHostnameVerifier = (HostnameVerifier)
+                        Class.forName("com.android.okhttp.internal.tls.OkHostnameVerifier")
+                        .getField("INSTANCE").get(null);
+            } catch (Exception e) {
+                throw new AssertionError("Failed to obtain okhttp HostnameVerifier", e);
+            }
+        }
 
         public static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory
                 .getDefault();
diff --git a/luni/src/main/java/javax/net/ssl/SSLEngine.java b/luni/src/main/java/javax/net/ssl/SSLEngine.java
index cbf02ac..f40f4b0 100644
--- a/luni/src/main/java/javax/net/ssl/SSLEngine.java
+++ b/luni/src/main/java/javax/net/ssl/SSLEngine.java
@@ -73,17 +73,17 @@
  *     <tbody>
  *         <tr>
  *             <td>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
  *             <td>9-19</td>
  *         </tr>
  *         <tr>
  *             <td>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
  *             <td>9-19</td>
  *         </tr>
  *         <tr>
  *             <td>SSL_DHE_DSS_WITH_DES_CBC_SHA</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
  *             <td>9-19</td>
  *         </tr>
  *         <tr>
@@ -178,32 +178,32 @@
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</td>
- *             <td>9+</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
+ *             <td>9-22</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA256</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_128_GCM_SHA256</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</td>
- *             <td>9+</td>
- *             <td>20+</td>
+ *             <td>9-22</td>
+ *             <td>20-22</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA256</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
diff --git a/luni/src/main/java/javax/net/ssl/SSLSocket.java b/luni/src/main/java/javax/net/ssl/SSLSocket.java
index dc406e1..c6906c5 100644
--- a/luni/src/main/java/javax/net/ssl/SSLSocket.java
+++ b/luni/src/main/java/javax/net/ssl/SSLSocket.java
@@ -117,17 +117,17 @@
  *     <tbody>
  *         <tr>
  *             <td>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
  *             <td>9-19</td>
  *         </tr>
  *         <tr>
  *             <td>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
  *             <td>9-19</td>
  *         </tr>
  *         <tr>
  *             <td>SSL_DHE_DSS_WITH_DES_CBC_SHA</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
  *             <td>9-19</td>
  *         </tr>
  *         <tr>
@@ -212,32 +212,32 @@
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</td>
- *             <td>9+</td>
- *             <td>9+</td>
+ *             <td>9-22</td>
+ *             <td>9-22</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA256</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_128_GCM_SHA256</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</td>
- *             <td>9+</td>
- *             <td>11+</td>
+ *             <td>9-22</td>
+ *             <td>11-22</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA256</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- *             <td>20+</td>
+ *             <td>20-22</td>
  *             <td></td>
  *         </tr>
  *         <tr>
@@ -639,14 +639,14 @@
  *         <tr>
  *             <td>DHE-DSS-AES128-SHA</td>
  *             <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</td>
- *             <td>1+</td>
- *             <td>1+</td>
+ *             <td>1-22</td>
+ *             <td>1-22</td>
  *         </tr>
  *         <tr>
  *             <td>DHE-DSS-AES256-SHA</td>
  *             <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</td>
- *             <td>1+</td>
- *             <td>1-8, 11+</td>
+ *             <td>1-22</td>
+ *             <td>1-8, 11-22</td>
  *         </tr>
  *         <tr>
  *             <td>DHE-RSA-AES128-SHA</td>
@@ -663,13 +663,13 @@
  *         <tr>
  *             <td>EDH-DSS-DES-CBC-SHA</td>
  *             <td>SSL_DHE_DSS_WITH_DES_CBC_SHA</td>
- *             <td>1+</td>
+ *             <td>1-22</td>
  *             <td>1-19</td>
  *         </tr>
  *         <tr>
  *             <td>EDH-DSS-DES-CBC3-SHA</td>
  *             <td>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</td>
- *             <td>1+</td>
+ *             <td>1-22</td>
  *             <td>1-19</td>
  *         </tr>
  *         <tr>
@@ -693,7 +693,7 @@
  *         <tr>
  *             <td>EXP-EDH-DSS-DES-CBC-SHA</td>
  *             <td>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</td>
- *             <td>1+</td>
+ *             <td>1-22</td>
  *             <td>1-19</td>
  *         </tr>
  *         <tr>
diff --git a/luni/src/main/java/javax/security/cert/X509Certificate.java b/luni/src/main/java/javax/security/cert/X509Certificate.java
index e85a556..5084ae0 100644
--- a/luni/src/main/java/javax/security/cert/X509Certificate.java
+++ b/luni/src/main/java/javax/security/cert/X509Certificate.java
@@ -51,7 +51,7 @@
             String classname = Security.getProperty("cert.provider.x509v1");
             Class cl = Class.forName(classname);
             constructor = cl.getConstructor(new Class[] {InputStream.class});
-        } catch (Throwable e) {
+        } catch (Exception|LinkageError e) {
         }
     }
 
@@ -80,7 +80,7 @@
             try {
                 return (X509Certificate)
                     constructor.newInstance(new Object[] {inStream});
-            } catch (Throwable e) {
+            } catch (ReflectiveOperationException e) {
                 throw new CertificateException(e.getMessage());
             }
         }
diff --git a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
index b65f412..1fbca2f 100644
--- a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
+++ b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
@@ -47,11 +47,30 @@
     /** <p>Debug flag to trace loading process.</p> */
     private static boolean debug = false;
 
-    /** <p>Cache properties for performance.</p> */
-    private static Properties cacheProps = new Properties();
+    /**
+     * <p>Cache properties for performance. Use a static class to avoid double-checked
+     * locking.</p>
+     */
+    private static class CacheHolder {
 
-    /** <p>First time requires initialization overhead.</p> */
-    private static boolean firstTime = true;
+        private static Properties cacheProps = new Properties();
+
+        static {
+            String javah = System.getProperty("java.home");
+            String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
+            File f = new File(configFile);
+            if (f.exists()) {
+                if (debug) debugPrintln("Read properties file " + f);
+                try {
+                    cacheProps.load(new FileInputStream(f));
+                } catch (Exception ex) {
+                    if (debug) {
+                        ex.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
 
     /** Default columns per line. */
     private static final int DEFAULT_LINE_LENGTH = 80;
@@ -177,22 +196,7 @@
 
         // try to read from $java.home/lib/jaxp.properties
         try {
-            String javah = System.getProperty("java.home");
-            String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
-            String factoryClassName = null;
-            if (firstTime) {
-                synchronized (cacheProps) {
-                    if (firstTime) {
-                        File f = new File(configFile);
-                        firstTime = false;
-                        if (f.exists()) {
-                            if (debug) debugPrintln("Read properties file " + f);
-                            cacheProps.load(new FileInputStream(f));
-                        }
-                    }
-                }
-            }
-            factoryClassName = cacheProps.getProperty(factoryId);
+            String factoryClassName = CacheHolder.cacheProps.getProperty(factoryId);
             if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
 
             if (factoryClassName != null) {
diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
index 636777c..0060612 100644
--- a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
@@ -49,14 +49,29 @@
     private static boolean debug = false;
 
     /**
-     * <p>Cache properties for performance.</p>
+     * <p>Cache properties for performance. Use a static class to avoid double-checked
+     * locking.</p>
      */
-    private static Properties cacheProps = new Properties();
+    private static class CacheHolder {
 
-    /**
-     * <p>First time requires initialization overhead.</p>
-     */
-    private static boolean firstTime = true;
+        private static Properties cacheProps = new Properties();
+
+        static {
+            String javah = System.getProperty("java.home");
+            String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
+            File f = new File(configFile);
+            if (f.exists()) {
+                if (debug) debugPrintln("Read properties file " + f);
+                try {
+                    cacheProps.load(new FileInputStream(f));
+                } catch (Exception ex) {
+                    if (debug) {
+                        ex.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
 
     /**
      * Default columns per line.
@@ -184,27 +199,9 @@
             }
         }
 
-        String javah = System.getProperty("java.home");
-        String configFile = javah + File.separator +
-        "lib" + File.separator + "jaxp.properties";
-
-        String factoryClassName = null ;
-
         // try to read from $java.home/lib/jaxp.properties
         try {
-            if(firstTime){
-                synchronized(cacheProps){
-                    if(firstTime){
-                        File f=new File( configFile );
-                        firstTime = false;
-                        if(f.exists()){
-                            if (debug) debugPrintln("Read properties file " + f);
-                            cacheProps.load(new FileInputStream(f));
-                        }
-                    }
-                }
-            }
-            factoryClassName = cacheProps.getProperty(propertyName);
+            String factoryClassName = CacheHolder.cacheProps.getProperty(propertyName);
             if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
 
             if (factoryClassName != null) {
diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
index 0113e7d..5a7663c 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
@@ -56,14 +56,29 @@
     }
 
     /**
-     * <p>Cache properties for performance.</p>
+     * <p>Cache properties for performance. Use a static class to avoid double-checked
+     * locking.</p>
      */
-    private static Properties cacheProps = new Properties();
+    private static class CacheHolder {
 
-    /**
-     * <p>First time requires initialization overhead.</p>
-     */
-    private static boolean firstTime = true;
+        private static Properties cacheProps = new Properties();
+
+        static {
+            String javah = System.getProperty("java.home");
+            String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
+            File f = new File(configFile);
+            if (f.exists()) {
+                if (debug) debugPrintln("Read properties file " + f);
+                try {
+                    cacheProps.load(new FileInputStream(f));
+                } catch (Exception ex) {
+                    if (debug) {
+                        ex.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
 
     /**
      * <p>Conditional debug printing.</p>
@@ -164,27 +179,9 @@
             e.printStackTrace();
         }
 
-        String javah = System.getProperty("java.home");
-        String configFile = javah + File.separator +
-        "lib" + File.separator + "jaxp.properties";
-
-        String factoryClassName = null ;
-
         // try to read from $java.home/lib/jaxp.properties
         try {
-            if(firstTime){
-                synchronized(cacheProps){
-                    if(firstTime){
-                        File f=new File( configFile );
-                        firstTime = false;
-                        if (f.exists()) {
-                            if (debug) debugPrintln("Read properties file " + f);
-                            cacheProps.load(new FileInputStream(f));
-                        }
-                    }
-                }
-            }
-            factoryClassName = cacheProps.getProperty(propertyName);
+            String factoryClassName = CacheHolder.cacheProps.getProperty(propertyName);
             if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
 
             if (factoryClassName != null) {
diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
index 3855654..509d0a0 100644
--- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java
+++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
@@ -16,48 +16,27 @@
 
 package libcore.icu;
 
-import java.util.Calendar;
-import java.util.Locale;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.ULocale;
+
+import java.text.FieldPosition;
 import java.util.TimeZone;
 import libcore.util.BasicLruCache;
 
+import static libcore.icu.DateUtilsBridge.FORMAT_UTC;
+
 /**
- * Exposes icu4c's DateIntervalFormat.
+ * Exposes icu4j's DateIntervalFormat.
  */
 public final class DateIntervalFormat {
 
-  // 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.
-  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;
-
-  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> {
+  static class FormatterCache extends BasicLruCache<String, com.ibm.icu.text.DateIntervalFormat> {
     FormatterCache() {
       super(8);
     }
-
-    protected void entryEvicted(String key, Long value) {
-      destroyDateIntervalFormat(value);
-    }
-  };
+  }
 
   private DateIntervalFormat() {
   }
@@ -67,131 +46,58 @@
     if ((flags & FORMAT_UTC) != 0) {
       olsonId = "UTC";
     }
+    // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID / pseudo-tz
+    // logic.
     TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault();
-    return formatDateRange(Locale.getDefault(), tz, startMs, endMs, flags);
+    com.ibm.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+    ULocale icuLocale = ULocale.getDefault();
+    return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags);
   }
 
   // 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) {
-    Calendar startCalendar = Calendar.getInstance(tz);
-    startCalendar.setTimeInMillis(startMs);
-
+  public static String formatDateRange(ULocale icuLocale, com.ibm.icu.util.TimeZone icuTimeZone,
+      long startMs, long endMs, int flags) {
+    Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs);
     Calendar endCalendar;
     if (startMs == endMs) {
       endCalendar = startCalendar;
     } else {
-      endCalendar = Calendar.getInstance(tz);
-      endCalendar.setTimeInMillis(endMs);
+      endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, 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
+    // This is not the behavior of icu4j's DateIntervalFormat, but it's the historical behavior
     // of Android's DateUtils.formatDateRange.
     if (startMs != endMs && endsAtMidnight &&
-        ((flags & FORMAT_SHOW_TIME) == 0 || dayDistance(startCalendar, endCalendar) <= 1)) {
+        ((flags & DateUtilsBridge.FORMAT_SHOW_TIME) == 0
+            || DateUtilsBridge.dayDistance(startCalendar, endCalendar) <= 1)) {
       endCalendar.roll(Calendar.DAY_OF_MONTH, false);
-      endMs -= DAY_IN_MS;
     }
 
-    String skeleton = toSkeleton(startCalendar, endCalendar, flags);
+    String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
     synchronized (CACHED_FORMATTERS) {
-      return formatDateInterval(getFormatter(skeleton, locale.toString(), tz.getID()), startMs, endMs);
+      com.ibm.icu.text.DateIntervalFormat formatter =
+          getFormatter(skeleton, icuLocale, icuTimeZone);
+      return formatter.format(startCalendar, endCalendar, new StringBuffer(),
+          new FieldPosition(0)).toString();
     }
   }
 
-  private static long getFormatter(String skeleton, String localeName, String tzName) {
-    String key = skeleton + "\t" + localeName + "\t" + tzName;
-    Long formatter = CACHED_FORMATTERS.get(key);
+  private static com.ibm.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale,
+      com.ibm.icu.util.TimeZone icuTimeZone) {
+    String key = skeleton + "\t" + locale + "\t" + icuTimeZone;
+    com.ibm.icu.text.DateIntervalFormat 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;
-    }
-
-    String monthPart = "MMMM";
-    if ((flags & FORMAT_NUMERIC_DATE) != 0) {
-      monthPart = "M";
-    } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
-      monthPart = "MMM";
-    }
-
-    String weekPart = "EEEE";
-    if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
-      weekPart = "EEE";
-    }
-
-    String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
-    if ((flags & FORMAT_24HOUR) != 0) {
-      timePart = "H";
-    } else if ((flags & FORMAT_12HOUR) != 0) {
-      timePart = "h";
-    }
-
-    // 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)) {
-      flags |= FORMAT_SHOW_DATE;
-    }
-
-    if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
-      flags &= (~FORMAT_SHOW_WEEKDAY);
-      flags &= (~FORMAT_SHOW_TIME);
-    }
-
-    if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
-      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) {
-        builder.append("y");
-      }
-      builder.append(monthPart);
-      if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
-        builder.append("d");
-      }
-    }
-    if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
-      builder.append(weekPart);
-    }
-    if ((flags & FORMAT_SHOW_TIME) != 0) {
-      builder.append(timePart);
-    }
-    return builder.toString();
+    formatter = com.ibm.icu.text.DateIntervalFormat.getInstance(skeleton, locale);
+    formatter.setTimeZone(icuTimeZone);
+    CACHED_FORMATTERS.put(key, formatter);
+    return formatter;
   }
 
   private static boolean isMidnight(Calendar c) {
@@ -201,39 +107,4 @@
         c.get(Calendar.MILLISECOND) == 0;
   }
 
-  private static boolean onTheHour(Calendar c) {
-    return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
-  }
-
-  private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
-    return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) ||
-        c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) ||
-        c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
-  }
-
-  private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
-    return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
-  }
-
-  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 dayDistance(Calendar c1, Calendar c2) {
-    return julianDay(c2) - julianDay(c1);
-  }
-
-  private static int julianDay(Calendar c) {
-    long utcMs = c.getTimeInMillis() + c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_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/DateTimeFormat.java b/luni/src/main/java/libcore/icu/DateTimeFormat.java
new file mode 100644
index 0000000..7323c26
--- /dev/null
+++ b/luni/src/main/java/libcore/icu/DateTimeFormat.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.icu;
+
+import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.text.DateTimePatternGenerator;
+import com.ibm.icu.text.DisplayContext;
+import com.ibm.icu.text.SimpleDateFormat;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.ULocale;
+
+import libcore.util.BasicLruCache;
+
+/**
+ * A formatter that outputs a single date/time.
+ */
+public class DateTimeFormat {
+  private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+  static class FormatterCache extends BasicLruCache<String, DateFormat> {
+    FormatterCache() {
+      super(8);
+    }
+  }
+
+  private DateTimeFormat() {
+  }
+
+  public static String format(ULocale icuLocale, Calendar time, int flags,
+      DisplayContext displayContext) {
+    String skeleton = DateUtilsBridge.toSkeleton(time, flags);
+    String key = skeleton + "\t" + icuLocale + "\t" + time.getTimeZone();
+    synchronized(CACHED_FORMATTERS) {
+      DateFormat formatter = CACHED_FORMATTERS.get(key);
+      if (formatter == null) {
+        DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(icuLocale);
+        formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale);
+        CACHED_FORMATTERS.put(key, formatter);
+      }
+      formatter.setContext(displayContext);
+      return formatter.format(time);
+    }
+  }
+}
diff --git a/luni/src/main/java/libcore/icu/DateUtilsBridge.java b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
new file mode 100644
index 0000000..88faa90
--- /dev/null
+++ b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 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.icu;
+
+import com.ibm.icu.impl.JavaTimeZone;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.GregorianCalendar;
+import com.ibm.icu.util.ULocale;
+
+/**
+ * Common methods and constants for the various ICU formatters used to support
+ * android.text.format.DateUtils.
+ */
+public final class DateUtilsBridge {
+  // 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.
+  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_RELATIVE = 0x40000;
+  public static final int FORMAT_ABBREV_ALL      = 0x80000;
+
+  /**
+   * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time of
+   * writing the libcore implementation is faster but restricted to 1902 - 2038.
+   * Callers must not modify the {@code tz} after calling this method.
+   */
+  public static com.ibm.icu.util.TimeZone icuTimeZone(java.util.TimeZone tz) {
+    JavaTimeZone javaTimeZone = new JavaTimeZone(tz, null);
+    javaTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
+    return javaTimeZone;
+  }
+
+  public static Calendar createIcuCalendar(com.ibm.icu.util.TimeZone icuTimeZone, ULocale icuLocale,
+      long timeInMillis) {
+    Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
+    calendar.setTimeInMillis(timeInMillis);
+    return calendar;
+  }
+
+  public static String toSkeleton(Calendar calendar, int flags) {
+    return toSkeleton(calendar, calendar, flags);
+  }
+
+  public 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;
+    }
+
+    String monthPart = "MMMM";
+    if ((flags & FORMAT_NUMERIC_DATE) != 0) {
+      monthPart = "M";
+    } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
+      monthPart = "MMM";
+    }
+
+    String weekPart = "EEEE";
+    if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
+      weekPart = "EEE";
+    }
+
+    String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
+    if ((flags & FORMAT_24HOUR) != 0) {
+      timePart = "H";
+    } else if ((flags & FORMAT_12HOUR) != 0) {
+      timePart = "h";
+    }
+
+    // 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)) {
+      flags |= FORMAT_SHOW_DATE;
+    }
+
+    if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
+      flags &= (~FORMAT_SHOW_WEEKDAY);
+      flags &= (~FORMAT_SHOW_TIME);
+    }
+
+    if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
+      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) {
+        builder.append("y");
+      }
+      builder.append(monthPart);
+      if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
+        builder.append("d");
+      }
+    }
+    if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
+      builder.append(weekPart);
+    }
+    if ((flags & FORMAT_SHOW_TIME) != 0) {
+      builder.append(timePart);
+    }
+    return builder.toString();
+  }
+
+  public static int dayDistance(Calendar c1, Calendar c2) {
+    return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
+  }
+
+  private static boolean onTheHour(Calendar c) {
+    return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
+  }
+
+  private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
+    return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) ||
+        c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) ||
+        c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
+  }
+
+  private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
+    return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
+  }
+
+  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) c.clone();
+    now.setTimeInMillis(System.currentTimeMillis());
+    return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
+  }
+}
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 0ef3f93..42def54 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -457,4 +457,7 @@
    * Returns a locale name, not a BCP-47 language tag. e.g. en_US not en-US.
    */
   public static native String getDefaultLocale();
+
+  /** Returns the TZData version as reported by ICU4C. */
+  public static native String getTZDataVersion();
 }
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index cca38e1..cf52b9c 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -88,15 +88,12 @@
     public String shortDateFormat4;
 
     // Used by DateFormat to implement 12- and 24-hour SHORT and MEDIUM.
+    // The first two are also used directly by frameworks code.
     public String timeFormat_hm;
     public String timeFormat_Hm;
     public String timeFormat_hms;
     public String timeFormat_Hms;
 
-    // Used by android.text.format.DateFormat.getTimeFormat.
-    public String timeFormat12; // "hh:mm a"
-    public String timeFormat24; // "HH:mm"
-
     // Used by DecimalFormatSymbols.
     public char zeroDigit;
     public char decimalSeparator;
@@ -213,12 +210,6 @@
         localeData.timeFormat_Hm = ICU.getBestDateTimePattern("Hm", locale);
         localeData.timeFormat_hms = ICU.getBestDateTimePattern("hms", locale);
         localeData.timeFormat_Hms = ICU.getBestDateTimePattern("Hms", locale);
-        // We could move callers over to the other fields, but these seem simpler and discourage
-        // people from shooting themselves in the foot by learning about patterns and skeletons.
-        // TODO: the right fix here is probably to move callers over to java.text.DateFormat,
-        // so nothing outside libcore references these any more.
-        localeData.timeFormat12 = localeData.timeFormat_hm;
-        localeData.timeFormat24 = localeData.timeFormat_Hm;
 
         // Fix up a couple of patterns.
         if (localeData.fullTimeFormat != null) {
diff --git a/luni/src/main/java/libcore/icu/NativeBreakIterator.java b/luni/src/main/java/libcore/icu/NativeBreakIterator.java
deleted file mode 100644
index 992aac2..0000000
--- a/luni/src/main/java/libcore/icu/NativeBreakIterator.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.icu;
-
-import java.text.CharacterIterator;
-import java.text.StringCharacterIterator;
-import java.util.Locale;
-
-public final class NativeBreakIterator implements Cloneable {
-    // Acceptable values for the 'type' field.
-    private static final int BI_CHAR_INSTANCE = 1;
-    private static final int BI_WORD_INSTANCE = 2;
-    private static final int BI_LINE_INSTANCE = 3;
-    private static final int BI_SENT_INSTANCE = 4;
-
-    // The address of the native peer.
-    // Uses of this must be manually synchronized to avoid native crashes.
-    private final long address;
-
-    private final int type;
-    private String string;
-    private CharacterIterator charIterator;
-
-    private NativeBreakIterator(long address, int type) {
-        this.address = address;
-        this.type = type;
-        this.charIterator = new StringCharacterIterator("");
-    }
-
-    @Override
-    public Object clone() {
-        long cloneAddr = cloneImpl(this.address);
-        NativeBreakIterator clone = new NativeBreakIterator(cloneAddr, this.type);
-        clone.string = this.string;
-        // The RI doesn't clone the CharacterIterator.
-        clone.charIterator = this.charIterator;
-        return clone;
-    }
-
-    @Override
-    public boolean equals(Object object) {
-        if (object == this) {
-            return true;
-        }
-        if (!(object instanceof NativeBreakIterator)) {
-            return false;
-        }
-        // TODO: is this sufficient? shouldn't we be checking the underlying rules?
-        NativeBreakIterator rhs = (NativeBreakIterator) object;
-        return type == rhs.type && charIterator.equals(rhs.charIterator);
-    }
-
-    @Override
-    public int hashCode() {
-        return 42; // No-one uses BreakIterator as a hash key.
-    }
-
-    @Override protected void finalize() throws Throwable {
-        try {
-            closeImpl(this.address);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    public int current() {
-        return currentImpl(this.address, this.string);
-    }
-
-    public int first() {
-        return firstImpl(this.address, this.string);
-    }
-
-    public int following(int offset) {
-        return followingImpl(this.address, this.string, offset);
-    }
-
-    public CharacterIterator getText() {
-        int newLocation = currentImpl(this.address, this.string);
-        this.charIterator.setIndex(newLocation);
-        return this.charIterator;
-    }
-
-    public int last() {
-        return lastImpl(this.address, this.string);
-    }
-
-    public int next(int n) {
-        return nextImpl(this.address, this.string, n);
-    }
-
-    public int next() {
-        return nextImpl(this.address, this.string, 1);
-    }
-
-    public int previous() {
-        return previousImpl(this.address, this.string);
-    }
-
-    public void setText(CharacterIterator newText) {
-        StringBuilder sb = new StringBuilder();
-        for (char c = newText.first(); c != CharacterIterator.DONE; c = newText.next()) {
-            sb.append(c);
-        }
-        setText(sb.toString(), newText);
-    }
-
-    public void setText(String newText) {
-        setText(newText, new StringCharacterIterator(newText));
-    }
-
-    private void setText(String s, CharacterIterator it) {
-        this.string = s;
-        this.charIterator = it;
-        setTextImpl(this.address, this.string);
-    }
-
-    public boolean hasText() {
-        return (string != null);
-    }
-
-    public boolean isBoundary(int offset) {
-        return isBoundaryImpl(this.address, this.string, offset);
-    }
-
-    public int preceding(int offset) {
-        return precedingImpl(this.address, this.string, offset);
-    }
-
-    public static NativeBreakIterator getCharacterInstance(Locale locale) {
-        return new NativeBreakIterator(getCharacterInstanceImpl(locale.toLanguageTag()), BI_CHAR_INSTANCE);
-    }
-
-    public static NativeBreakIterator getLineInstance(Locale locale) {
-        return new NativeBreakIterator(getLineInstanceImpl(locale.toLanguageTag()), BI_LINE_INSTANCE);
-    }
-
-    public static NativeBreakIterator getSentenceInstance(Locale locale) {
-        return new NativeBreakIterator(getSentenceInstanceImpl(locale.toLanguageTag()), BI_SENT_INSTANCE);
-    }
-
-    public static NativeBreakIterator getWordInstance(Locale locale) {
-        return new NativeBreakIterator(getWordInstanceImpl(locale.toLanguageTag()), BI_WORD_INSTANCE);
-    }
-
-    private static native long getCharacterInstanceImpl(String locale);
-    private static native long getWordInstanceImpl(String locale);
-    private static native long getLineInstanceImpl(String locale);
-    private static native long getSentenceInstanceImpl(String locale);
-    private static synchronized native long cloneImpl(long address);
-
-    private static synchronized native void closeImpl(long address);
-
-    private static synchronized native void setTextImpl(long address, String text);
-    private static synchronized native int precedingImpl(long address, String text, int offset);
-    private static synchronized native boolean isBoundaryImpl(long address, String text, int offset);
-    private static synchronized native int nextImpl(long address, String text, int n);
-    private static synchronized native int previousImpl(long address, String text);
-    private static synchronized native int currentImpl(long address, String text);
-    private static synchronized native int firstImpl(long address, String text);
-    private static synchronized native int followingImpl(long address, String text, int offset);
-    private static synchronized native int lastImpl(long address, String text);
-}
diff --git a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
new file mode 100644
index 0000000..e2afa61
--- /dev/null
+++ b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2015 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.icu;
+
+import java.util.Locale;
+import libcore.util.BasicLruCache;
+
+import com.ibm.icu.text.DisplayContext;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.ULocale;
+
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_ALL;
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_MONTH;
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static libcore.icu.DateUtilsBridge.FORMAT_NO_YEAR;
+import static libcore.icu.DateUtilsBridge.FORMAT_NUMERIC_DATE;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_DATE;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_TIME;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_YEAR;
+
+/**
+ * Exposes icu4j's RelativeDateTimeFormatter.
+ */
+public final class RelativeDateTimeFormatter {
+
+  public static final long SECOND_IN_MILLIS = 1000;
+  public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
+  public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
+  public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
+  public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+  // YEAR_IN_MILLIS considers 364 days as a year. However, since this
+  // constant comes from public API in DateUtils, it cannot be fixed here.
+  public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;
+
+  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, com.ibm.icu.text.RelativeDateTimeFormatter> {
+    FormatterCache() {
+      super(8);
+    }
+  }
+
+  private RelativeDateTimeFormatter() {
+  }
+
+  /**
+   * This is the internal API that implements the functionality of
+   * DateUtils.getRelativeTimeSpanString(long, long, long, int), which is to
+   * return a string describing 'time' as a time relative to 'now' such as
+   * '5 minutes ago', or 'in 2 days'. More examples can be found in DateUtils'
+   * doc.
+   *
+   * In the implementation below, it selects the appropriate time unit based on
+   * the elapsed time between time' and 'now', e.g. minutes, days and etc.
+   * Callers may also specify the desired minimum resolution to show in the
+   * result. For example, '45 minutes ago' will become '0 hours ago' when
+   * minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to
+   * display, it calls icu4j's RelativeDateTimeFormatter to format the actual
+   * string according to the given locale.
+   *
+   * Note that when minResolution is set to DAY_IN_MILLIS, it returns the
+   * result depending on the actual date difference. For example, it will
+   * return 'Yesterday' even if 'time' was less than 24 hours ago but falling
+   * onto a different calendar day.
+   *
+   * It takes two additional parameters of Locale and TimeZone than the
+   * DateUtils' API. Caller must specify the locale and timezone.
+   * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
+   * the abbreviated forms when available. When 'time' equals to 'now', it
+   * always // returns a string like '0 seconds/minutes/... ago' according to
+   * minResolution.
+   */
+  public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+      long now, long minResolution, int flags) {
+    // Android has been inconsistent about capitalization in the past. e.g. bug http://b/20247811.
+    // Now we capitalize everything consistently.
+    final DisplayContext displayContext = DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
+    return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags, displayContext);
+  }
+
+  public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+      long now, long minResolution, int flags, DisplayContext displayContext) {
+    if (locale == null) {
+      throw new NullPointerException("locale == null");
+    }
+    if (tz == null) {
+      throw new NullPointerException("tz == null");
+    }
+    ULocale icuLocale = ULocale.forLocale(locale);
+    com.ibm.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+    return getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution, flags,
+        displayContext);
+  }
+
+  private static String getRelativeTimeSpanString(ULocale icuLocale,
+      com.ibm.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution, int flags,
+      DisplayContext displayContext) {
+
+    long duration = Math.abs(now - time);
+    boolean past = (now >= time);
+
+    com.ibm.icu.text.RelativeDateTimeFormatter.Style style;
+    if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+      style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+    } else {
+      style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.LONG;
+    }
+
+    com.ibm.icu.text.RelativeDateTimeFormatter.Direction direction;
+    if (past) {
+      direction = com.ibm.icu.text.RelativeDateTimeFormatter.Direction.LAST;
+    } else {
+      direction = com.ibm.icu.text.RelativeDateTimeFormatter.Direction.NEXT;
+    }
+
+    // 'relative' defaults to true as we are generating relative time span
+    // string. It will be set to false when we try to display strings without
+    // a quantity, such as 'Yesterday', etc.
+    boolean relative = true;
+    int count;
+    com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit unit;
+    com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit aunit = null;
+
+    if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {
+      count = (int)(duration / SECOND_IN_MILLIS);
+      unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.SECONDS;
+    } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {
+      count = (int)(duration / MINUTE_IN_MILLIS);
+      unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.MINUTES;
+    } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {
+      // Even if 'time' actually happened yesterday, we don't format it as
+      // "Yesterday" in this case. Unless the duration is longer than a day,
+      // or minResolution is specified as DAY_IN_MILLIS by user.
+      count = (int)(duration / HOUR_IN_MILLIS);
+      unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.HOURS;
+    } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
+      count = Math.abs(dayDistance(icuTimeZone, time, now));
+      unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.DAYS;
+
+      if (count == 2) {
+        // Some locales have special terms for "2 days ago". Return them if
+        // available. Note that we cannot set up direction and unit here and
+        // make it fall through to use the call near the end of the function,
+        // because for locales that don't have special terms for "2 days ago",
+        // icu4j returns an empty string instead of falling back to strings
+        // like "2 days ago".
+        String str;
+        if (past) {
+          synchronized (CACHED_FORMATTERS) {
+            str = getFormatter(icuLocale, style, displayContext)
+                .format(
+                    com.ibm.icu.text.RelativeDateTimeFormatter.Direction.LAST_2,
+                    com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+          }
+        } else {
+          synchronized (CACHED_FORMATTERS) {
+            str = getFormatter(icuLocale, style, displayContext)
+                .format(
+                    com.ibm.icu.text.RelativeDateTimeFormatter.Direction.NEXT_2,
+                    com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+          }
+        }
+        if (str != null && !str.isEmpty()) {
+          return str;
+        }
+        // Fall back to show something like "2 days ago".
+      } else if (count == 1) {
+        // Show "Yesterday / Tomorrow" instead of "1 day ago / In 1 day".
+        aunit = com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+        relative = false;
+      } else if (count == 0) {
+        // Show "Today" if time and now are on the same day.
+        aunit = com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+        direction = com.ibm.icu.text.RelativeDateTimeFormatter.Direction.THIS;
+        relative = false;
+      }
+    } else if (minResolution == WEEK_IN_MILLIS) {
+      count = (int)(duration / WEEK_IN_MILLIS);
+      unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.WEEKS;
+    } else {
+      Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+      // The duration is longer than a week and minResolution is not
+      // WEEK_IN_MILLIS. Return the absolute date instead of relative time.
+
+      // Bug 19822016:
+      // If user doesn't supply the year display flag, we need to explicitly
+      // set that to show / hide the year based on time and now. Otherwise
+      // formatDateRange() would determine that based on the current system
+      // time and may give wrong results.
+      if ((flags & (FORMAT_NO_YEAR | FORMAT_SHOW_YEAR)) == 0) {
+        Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
+
+        if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+          flags |= FORMAT_SHOW_YEAR;
+        } else {
+          flags |= FORMAT_NO_YEAR;
+        }
+      }
+      return DateTimeFormat.format(icuLocale, timeCalendar, flags, displayContext);
+    }
+
+    synchronized (CACHED_FORMATTERS) {
+      com.ibm.icu.text.RelativeDateTimeFormatter formatter =
+          getFormatter(icuLocale, style, displayContext);
+      if (relative) {
+        return formatter.format(count, direction, unit);
+      } else {
+        return formatter.format(direction, aunit);
+      }
+    }
+  }
+
+  /**
+   * This is the internal API that implements
+   * DateUtils.getRelativeDateTimeString(long, long, long, long, int), which is
+   * to return a string describing 'time' as a time relative to 'now', formatted
+   * like '[relative time/date], [time]'. More examples can be found in
+   * DateUtils' doc.
+   *
+   * The function is similar to getRelativeTimeSpanString, but it always
+   * appends the absolute time to the relative time string to return
+   * '[relative time/date clause], [absolute time clause]'. It also takes an
+   * extra parameter transitionResolution to determine the format of the date
+   * clause. When the elapsed time is less than the transition resolution, it
+   * displays the relative time string. Otherwise, it gives the absolute
+   * numeric date string as the date clause. With the date and time clauses, it
+   * relies on icu4j's RelativeDateTimeFormatter::combineDateAndTime() to
+   * concatenate the two.
+   *
+   * It takes two additional parameters of Locale and TimeZone than the
+   * DateUtils' API. Caller must specify the locale and timezone.
+   * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
+   * the abbreviated forms when they are available.
+   *
+   * Bug 5252772: Since the absolute time will always be part of the result,
+   * minResolution will be set to at least DAY_IN_MILLIS to correctly indicate
+   * the date difference. For example, when it's 1:30 AM, it will return
+   * 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null,
+   * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2
+   * hours ago, 11:30 PM' even with minResolution being HOUR_IN_MILLIS.
+   */
+  public static String getRelativeDateTimeString(Locale locale, java.util.TimeZone tz, long time,
+      long now, long minResolution, long transitionResolution, int flags) {
+
+    if (locale == null) {
+      throw new NullPointerException("locale == null");
+    }
+    if (tz == null) {
+      throw new NullPointerException("tz == null");
+    }
+    ULocale icuLocale = ULocale.forLocale(locale);
+    com.ibm.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+
+    long duration = Math.abs(now - time);
+    // It doesn't make much sense to have results like: "1 week ago, 10:50 AM".
+    if (transitionResolution > WEEK_IN_MILLIS) {
+        transitionResolution = WEEK_IN_MILLIS;
+    }
+    com.ibm.icu.text.RelativeDateTimeFormatter.Style style;
+    if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+        style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+    } else {
+        style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.LONG;
+    }
+
+    Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+    Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
+
+    int days = Math.abs(DateUtilsBridge.dayDistance(timeCalendar, nowCalendar));
+
+    // Now get the date clause, either in relative format or the actual date.
+    String dateClause;
+    if (duration < transitionResolution) {
+      // This is to fix bug 5252772. If there is any date difference, we should
+      // promote the minResolution to DAY_IN_MILLIS so that it can display the
+      // date instead of "x hours/minutes ago, [time]".
+      if (days > 0 && minResolution < DAY_IN_MILLIS) {
+         minResolution = DAY_IN_MILLIS;
+      }
+      dateClause = getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution,
+          flags, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+    } else {
+      // We always use fixed flags to format the date clause. User-supplied
+      // flags are ignored.
+      if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+        // Different years
+        flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
+      } else {
+        // Default
+        flags = FORMAT_SHOW_DATE | FORMAT_NO_YEAR | FORMAT_ABBREV_MONTH;
+      }
+
+      dateClause = DateTimeFormat.format(icuLocale, timeCalendar, flags,
+          DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+    }
+
+    String timeClause = DateTimeFormat.format(icuLocale, timeCalendar, FORMAT_SHOW_TIME,
+        DisplayContext.CAPITALIZATION_NONE);
+
+    // icu4j also has other options available to control the capitalization. We are currently using
+    // the _NONE option only.
+    DisplayContext capitalizationContext = DisplayContext.CAPITALIZATION_NONE;
+
+    // Combine the two clauses, such as '5 days ago, 10:50 AM'.
+    synchronized (CACHED_FORMATTERS) {
+      return getFormatter(icuLocale, style, capitalizationContext)
+              .combineDateAndTime(dateClause, timeClause);
+    }
+  }
+
+  /**
+   * getFormatter() caches the RelativeDateTimeFormatter instances based on
+   * the combination of localeName, sytle and capitalizationContext. It
+   * should always be used along with the action of the formatter in a
+   * synchronized block, because otherwise the formatter returned by
+   * getFormatter() may have been evicted by the time of the call to
+   * formatter->action().
+   */
+  private static com.ibm.icu.text.RelativeDateTimeFormatter getFormatter(
+      ULocale locale, com.ibm.icu.text.RelativeDateTimeFormatter.Style style,
+      DisplayContext displayContext) {
+    String key = locale + "\t" + style + "\t" + displayContext;
+    com.ibm.icu.text.RelativeDateTimeFormatter formatter = CACHED_FORMATTERS.get(key);
+    if (formatter == null) {
+      formatter = com.ibm.icu.text.RelativeDateTimeFormatter.getInstance(
+          locale, null, style, displayContext);
+      CACHED_FORMATTERS.put(key, formatter);
+    }
+    return formatter;
+  }
+
+  // Return the date difference for the two times in a given timezone.
+  private static int dayDistance(com.ibm.icu.util.TimeZone icuTimeZone, long startTime,
+      long endTime) {
+    return julianDay(icuTimeZone, endTime) - julianDay(icuTimeZone, startTime);
+  }
+
+  private static int julianDay(com.ibm.icu.util.TimeZone icuTimeZone, long time) {
+    long utcMs = time + icuTimeZone.getOffset(time);
+    return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
+  }
+}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index b3dc74b..532493a 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -83,7 +83,7 @@
             // The usual case is that this _isn't_ a socket, so the getsockopt(2) call in
             // isLingerSocket will throw, and that's really expensive. Try to avoid asking
             // if we don't care.
-            if (fd.isSocket()) {
+            if (fd.isSocket$()) {
                 if (isLingerSocket(fd)) {
                     // If the fd is a socket with SO_LINGER set, we might block indefinitely.
                     // We allow non-linger sockets so that apps can close their network
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 584fd58..5c0c8fd 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -54,10 +54,12 @@
     public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); }
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
+    public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.bind(fd, address); }
     public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
     public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); }
     public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); }
     public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.connect(fd, address, port); }
+    public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.connect(fd, address); }
     public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return os.dup(oldFd); }
     public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return os.dup2(oldFd, newFd); }
     public String[] environ() { return os.environ(); }
@@ -65,9 +67,9 @@
     public void execve(String filename, String[] argv, String[] envp) throws ErrnoException { os.execve(filename, argv, envp); }
     public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { os.fchmod(fd, mode); }
     public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { os.fchown(fd, uid, gid); }
-    public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
-    public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return os.fcntlLong(fd, cmd, arg); }
     public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException { return os.fcntlFlock(fd, cmd, arg); }
+    public int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException { return os.fcntlInt(fd, cmd, arg); }
+    public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
     public void fdatasync(FileDescriptor fd) throws ErrnoException { os.fdatasync(fd); }
     public StructStat fstat(FileDescriptor fd) throws ErrnoException { return os.fstat(fd); }
     public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException { return os.fstatvfs(fd); }
@@ -80,6 +82,7 @@
     public String getenv(String name) { return os.getenv(name); }
     public String getnameinfo(InetAddress address, int flags) throws GaiException { return os.getnameinfo(address, flags); }
     public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return os.getpeername(fd); }
+    public int getpgid(int pid) throws ErrnoException { return os.getpgid(pid); }
     public int getpid() { return os.getpid(); }
     public int getppid() { return os.getppid(); }
     public StructPasswd getpwnam(String name) throws ErrnoException { return os.getpwnam(name); }
@@ -113,7 +116,7 @@
     public void munlock(long address, long byteCount) throws ErrnoException { os.munlock(address, byteCount); }
     public void munmap(long address, long byteCount) throws ErrnoException { os.munmap(address, byteCount); }
     public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { return os.open(path, flags, mode); }
-    public FileDescriptor[] pipe() throws ErrnoException { return os.pipe(); }
+    public FileDescriptor[] pipe2(int flags) throws ErrnoException { return os.pipe2(flags); }
     public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return os.poll(fds, timeoutMs); }
     public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { os.posix_fallocate(fd, offset, length); }
     public int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException { return os.prctl(option, arg2, arg3, arg4, arg5); };
@@ -132,10 +135,14 @@
     public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); }
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
+    public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
     public void setegid(int egid) throws ErrnoException { os.setegid(egid); }
     public void setenv(String name, String value, boolean overwrite) throws ErrnoException { os.setenv(name, value, overwrite); }
     public void seteuid(int euid) throws ErrnoException { os.seteuid(euid); }
     public void setgid(int gid) throws ErrnoException { os.setgid(gid); }
+    public void setpgid(int pid, int pgid) throws ErrnoException { os.setpgid(pid, pgid); }
+    public void setregid(int rgid, int egid) throws ErrnoException { os.setregid(rgid, egid); }
+    public void setreuid(int ruid, int euid) throws ErrnoException { os.setregid(ruid, euid); }
     public int setsid() throws ErrnoException { return os.setsid(); }
     public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptByte(fd, level, option, value); }
     public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { os.setsockoptIfreq(fd, level, option, value); }
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index acc8d4f..fcb30dd 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -225,11 +225,7 @@
             if (!fd.valid()) {
                 throw new SocketException("Socket closed");
             }
-            if (errnoException.errno == EINTR) {
-                return false; // Punt and ask the caller to try again.
-            } else {
-                cause = errnoException;
-            }
+            cause = errnoException;
         }
         String detail = connectDetail(inetAddress, port, timeoutMs, cause);
         if (cause.errno == ETIMEDOUT) {
diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java
index 5a19f17..b01759d 100644
--- a/luni/src/main/java/libcore/io/IoUtils.java
+++ b/luni/src/main/java/libcore/io/IoUtils.java
@@ -30,8 +30,6 @@
 import static android.system.OsConstants.*;
 
 public final class IoUtils {
-    private static final Random TEMPORARY_DIRECTORY_PRNG = new Random();
-
     private IoUtils() {
     }
 
@@ -96,7 +94,7 @@
             } else {
                 flags &= ~O_NONBLOCK;
             }
-            Libcore.os.fcntlLong(fd, F_SETFL, flags);
+            Libcore.os.fcntlInt(fd, F_SETFL, flags);
         } catch (ErrnoException errnoException) {
             throw errnoException.rethrowAsIOException();
         }
@@ -142,7 +140,7 @@
      */
     public static File createTemporaryDirectory(String prefix) {
         while (true) {
-            String candidateName = prefix + TEMPORARY_DIRECTORY_PRNG.nextInt();
+            String candidateName = prefix + Math.randomIntInternal();
             File result = new File(System.getProperty("java.io.tmpdir"), candidateName);
             if (result.mkdir()) {
                 return result;
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 9f080a6..987d331 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -45,10 +45,12 @@
     public boolean access(String path, int mode) throws ErrnoException;
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+    public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
     public void chmod(String path, int mode) throws ErrnoException;
     public void chown(String path, int uid, int gid) throws ErrnoException;
     public void close(FileDescriptor fd) throws ErrnoException;
     public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+    public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
     public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
     public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
     public String[] environ();
@@ -56,9 +58,9 @@
     public void execve(String filename, String[] argv, String[] envp) throws ErrnoException;
     public void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
     public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
-    public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
-    public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
     public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException;
+    public int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException;
+    public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
     public void fdatasync(FileDescriptor fd) throws ErrnoException;
     public StructStat fstat(FileDescriptor fd) throws ErrnoException;
     public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException;
@@ -72,6 +74,7 @@
     /* TODO: break into getnameinfoHost and getnameinfoService? */
     public String getnameinfo(InetAddress address, int flags) throws GaiException;
     public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
+    public int getpgid(int pid) throws ErrnoException;
     public int getpid();
     public int getppid();
     public StructPasswd getpwnam(String name) throws ErrnoException;
@@ -105,7 +108,7 @@
     public void munlock(long address, long byteCount) throws ErrnoException;
     public void munmap(long address, long byteCount) throws ErrnoException;
     public FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
-    public FileDescriptor[] pipe() throws ErrnoException;
+    public FileDescriptor[] pipe2(int flags) throws ErrnoException;
     /* TODO: if we used the non-standard ppoll(2) behind the scenes, we could take a long timeout. */
     public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
     public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
@@ -124,11 +127,15 @@
     public void rename(String oldPath, String newPath) throws ErrnoException;
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+    public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException;
     public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
     public void setegid(int egid) throws ErrnoException;
     public void setenv(String name, String value, boolean overwrite) throws ErrnoException;
     public void seteuid(int euid) throws ErrnoException;
     public void setgid(int gid) throws ErrnoException;
+    public void setpgid(int pid, int pgid) throws ErrnoException;
+    public void setregid(int rgid, int egid) throws ErrnoException;
+    public void setreuid(int ruid, int euid) throws ErrnoException;
     public int setsid() throws ErrnoException;
     public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
     public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 5bd1b06..d680200 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -48,10 +48,12 @@
     public native boolean access(String path, int mode) throws ErrnoException;
     public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+    public native void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
     public native void chmod(String path, int mode) throws ErrnoException;
     public native void chown(String path, int uid, int gid) throws ErrnoException;
     public native void close(FileDescriptor fd) throws ErrnoException;
     public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+    public native void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
     public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
     public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
     public native String[] environ();
@@ -59,9 +61,9 @@
     public native void execve(String filename, String[] argv, String[] envp) throws ErrnoException;
     public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
     public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
-    public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
-    public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
     public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException;
+    public native int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException;
+    public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
     public native void fdatasync(FileDescriptor fd) throws ErrnoException;
     public native StructStat fstat(FileDescriptor fd) throws ErrnoException;
     public native StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException;
@@ -74,6 +76,7 @@
     public native String getenv(String name);
     public native String getnameinfo(InetAddress address, int flags) throws GaiException;
     public native SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
+    public native int getpgid(int pid);
     public native int getpid();
     public native int getppid();
     public native StructPasswd getpwnam(String name) throws ErrnoException;
@@ -107,7 +110,7 @@
     public native void munlock(long address, long byteCount) throws ErrnoException;
     public native void munmap(long address, long byteCount) throws ErrnoException;
     public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
-    public native FileDescriptor[] pipe() throws ErrnoException;
+    public native FileDescriptor[] pipe2(int flags) throws ErrnoException;
     public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
     public native void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
     public native int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException;
@@ -205,11 +208,18 @@
         // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
         return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
     }
+    public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException {
+        return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, address);
+    }
     private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+    private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException;
     public native void setegid(int egid) throws ErrnoException;
     public native void setenv(String name, String value, boolean overwrite) throws ErrnoException;
     public native void seteuid(int euid) throws ErrnoException;
     public native void setgid(int gid) throws ErrnoException;
+    public native void setpgid(int pid, int pgid) throws ErrnoException;
+    public native void setregid(int rgid, int egid) throws ErrnoException;
+    public native void setreuid(int ruid, int euid) throws ErrnoException;
     public native int setsid() throws ErrnoException;
     public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
     public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index 125ea87..e36b3d1 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -16,13 +16,8 @@
 
 package libcore.net;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Properties;
 
 /**
  * Utilities for dealing with MIME types.
@@ -49,6 +44,7 @@
 
         add("application/andrew-inset", "ez");
         add("application/dsptype", "tsp");
+        add("application/epub+zip", "epub");
         add("application/hta", "hta");
         add("application/mac-binhex40", "hqx");
         add("application/mathematica", "nb");
@@ -73,6 +69,8 @@
         add("application/vnd.oasis.opendocument.graphics", "odg");
         add("application/vnd.oasis.opendocument.graphics-template", "otg");
         add("application/vnd.oasis.opendocument.image", "odi");
+        add("application/vnd.oasis.opendocument.presentation", "odp");
+        add("application/vnd.oasis.opendocument.presentation-template", "otp");
         add("application/vnd.oasis.opendocument.spreadsheet", "ods");
         add("application/vnd.oasis.opendocument.spreadsheet-template", "ots");
         add("application/vnd.oasis.opendocument.text", "odt");
@@ -148,6 +146,7 @@
         add("application/x-gtar", "gtar");
         add("application/x-gtar", "taz");
         add("application/x-hdf", "hdf");
+        add("application/x-hwp", "hwp"); // http://b/18788282.
         add("application/x-ica", "ica");
         add("application/x-internet-signup", "ins");
         add("application/x-internet-signup", "isp");
@@ -378,7 +377,6 @@
         add("video/x-webex", "wrf");
         add("x-conference/x-cooltalk", "ice");
         add("x-epoc/x-sisx-app", "sisx");
-        applyOverrides();
     }
 
     private static void add(String mimeType, String extension) {
@@ -395,61 +393,6 @@
         }
     }
 
-    private static InputStream getContentTypesPropertiesStream() {
-        // User override?
-        String userTable = System.getProperty("content.types.user.table");
-        if (userTable != null) {
-            File f = new File(userTable);
-            if (f.exists()) {
-                try {
-                    return new FileInputStream(f);
-                } catch (IOException ignored) {
-                }
-            }
-        }
-
-        // Standard location?
-        File f = new File(System.getProperty("java.home"), "lib" + File.separator + "content-types.properties");
-        if (f.exists()) {
-            try {
-                return new FileInputStream(f);
-            } catch (IOException ignored) {
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * This isn't what the RI does. The RI doesn't have hard-coded defaults, so supplying your
-     * own "content.types.user.table" means you don't get any of the built-ins, and the built-ins
-     * come from "$JAVA_HOME/lib/content-types.properties".
-     */
-    private static void applyOverrides() {
-        // Get the appropriate InputStream to read overrides from, if any.
-        InputStream stream = getContentTypesPropertiesStream();
-        if (stream == null) {
-            return;
-        }
-
-        try {
-            try {
-                // Read the properties file...
-                Properties overrides = new Properties();
-                overrides.load(stream);
-                // And translate its mapping to ours...
-                for (Map.Entry<Object, Object> entry : overrides.entrySet()) {
-                    String extension = (String) entry.getKey();
-                    String mimeType = (String) entry.getValue();
-                    add(mimeType, extension);
-                }
-            } finally {
-                stream.close();
-            }
-        } catch (IOException ignored) {
-        }
-    }
-
     private MimeUtils() {
     }
 
diff --git a/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java
new file mode 100644
index 0000000..b1a41e8
--- /dev/null
+++ b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.net;
+
+import libcore.net.url.FtpURLConnection;
+
+/**
+ * Network security policy for this process/application.
+ *
+ * <p>Network stacks/components are expected to honor this policy. Components which can use the
+ * Android framework API should be accessing this policy via the framework's
+ * {@code android.security.NetworkSecurityPolicy} instead of via this class.
+ *
+ * <p>The policy currently consists of a single flag: whether cleartext network traffic is
+ * permitted. See {@link #isCleartextTrafficPermitted()}.
+ */
+public class NetworkSecurityPolicy {
+
+    private static volatile boolean cleartextTrafficPermitted = true;
+
+    /**
+     * Returns whether cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP -- without TLS or
+     * STARTTLS) is permitted for this process.
+     *
+     * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP
+     * stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use
+     * cleartext traffic. Third-party libraries are encouraged to do the same.
+     *
+     * <p>This flag is honored on a best effort basis because it's impossible to prevent all
+     * cleartext traffic from an application given the level of access provided to applications on
+     * Android. For example, there's no expectation that {@link java.net.Socket} API will honor this
+     * flag. Luckily, most network traffic from apps is handled by higher-level network stacks which
+     * can be made to honor this flag. Platform-provided network stacks (e.g. HTTP and FTP) honor
+     * this flag from day one, and well-established third-party network stacks will eventually
+     * honor it.
+     *
+     * <p>See {@link FtpURLConnection} for an example of honoring this flag.
+     */
+    public static boolean isCleartextTrafficPermitted() {
+        return cleartextTrafficPermitted;
+    }
+
+    /**
+     * Sets whether cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP -- without TLS or
+     * STARTTLS) is permitted for this process.
+     *
+     * @see #isCleartextTrafficPermitted()
+     */
+    public static void setCleartextTrafficPermitted(boolean permitted) {
+        cleartextTrafficPermitted = permitted;
+    }
+}
diff --git a/luni/src/main/java/libcore/net/http/ResponseUtils.java b/luni/src/main/java/libcore/net/http/ResponseUtils.java
new file mode 100644
index 0000000..c892b53
--- /dev/null
+++ b/luni/src/main/java/libcore/net/http/ResponseUtils.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.net.http;
+
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public class ResponseUtils {
+  /**
+   * Returns the response charset of a HTTP response based on the {@code Content-Type} of
+   * the response (see RFC 7230). If the {@code Content-Type} header is missing or invalid,
+   * the response is assumed to be encoded as {@code UTF-8}. Note that a charset usually
+   * makes sense only for {@code "text/plain"} and other "text based" responses.
+   *
+   * @throws IllegalCharsetNameException if the response specified charset is illegal.
+   * @throws UnsupportedCharsetException if the response specified charset is unsupported.
+   */
+  public static Charset responseCharset(String contentTypeHeader)
+      throws IllegalCharsetNameException, UnsupportedCharsetException {
+    Charset responseCharset = StandardCharsets.UTF_8;
+    if (contentTypeHeader != null) {
+      Map<String, String> contentTypeParams = parseContentTypeParameters(contentTypeHeader);
+      String charsetParameter = contentTypeParams.get("charset");
+      if (charsetParameter != null) {
+        responseCharset = Charset.forName(charsetParameter);
+      }
+    }
+
+    return responseCharset;
+  }
+
+  /**
+   * Parse content-type parameters. The format of this header is roughly :
+   * {@code type/subtype; param1=value1; param2=value2 ...} where each of the
+   * parameters are optional. Parsing is lenient, malformed parameters are ignored.
+   *
+   * Parameter keys & values are trimmed of whitespace and keys are converted to
+   * lower case.
+   */
+  private static Map<String, String> parseContentTypeParameters(String contentTypeHeader) {
+    Map<String, String> parameters = Collections.EMPTY_MAP;
+
+    String[] fields = contentTypeHeader.split(";");
+    if (fields.length > 1) {
+      parameters = new HashMap<>();
+      // Ignore the first element in the array (the type/subtype).
+      for (int i = 1; i < fields.length; ++i) {
+        final String parameter = fields[i];
+        if (!parameter.isEmpty()) {
+          final String[] components = parameter.split("=");
+          if (components.length != 2) {
+            continue;
+          }
+
+          final String key = components[0].trim().toLowerCase();
+          final String value = components[1].trim();
+          if (key.isEmpty() || value.isEmpty()) {
+            continue;
+          }
+
+          parameters.put(key, value);
+        }
+      }
+    }
+
+    return parameters;
+  }
+}
diff --git a/luni/src/main/java/libcore/net/url/FtpURLConnection.java b/luni/src/main/java/libcore/net/url/FtpURLConnection.java
index 7594c3a..021bfa2 100644
--- a/luni/src/main/java/libcore/net/url/FtpURLConnection.java
+++ b/luni/src/main/java/libcore/net/url/FtpURLConnection.java
@@ -17,6 +17,7 @@
 
 package libcore.net.url;
 
+import libcore.net.NetworkSecurityPolicy;
 import java.io.BufferedInputStream;
 import java.io.EOFException;
 import java.io.FileNotFoundException;
@@ -103,9 +104,15 @@
      *
      * @param url
      */
-    protected FtpURLConnection(URL url) {
+    protected FtpURLConnection(URL url) throws IOException {
         super(url);
         hostName = url.getHost();
+        if (!NetworkSecurityPolicy.isCleartextTrafficPermitted()) {
+            // Cleartext network traffic is not permitted -- refuse this connection.
+            throw new IOException("Cleartext traffic not permitted: "
+                    + url.getProtocol() + "://" + hostName
+                    + ((url.getPort() >= 0) ? (":" + url.getPort()) : ""));
+        }
         String parse = url.getUserInfo();
         if (parse != null) {
             int split = parse.indexOf(':');
@@ -118,7 +125,7 @@
         }
         uri = null;
         try {
-            uri = url.toURI();
+            uri = url.toURILenient();
         } catch (URISyntaxException e) {
             // do nothing.
         }
@@ -130,7 +137,7 @@
      * @param url
      * @param proxy
      */
-    protected FtpURLConnection(URL url, Proxy proxy) {
+    protected FtpURLConnection(URL url, Proxy proxy) throws IOException {
         this(url);
         this.proxy = proxy;
     }
diff --git a/luni/src/main/java/java/nio/charset/Charsets.java b/luni/src/main/java/libcore/util/CharsetUtils.java
similarity index 96%
rename from luni/src/main/java/java/nio/charset/Charsets.java
rename to luni/src/main/java/libcore/util/CharsetUtils.java
index 3dede7a..2e426c4 100644
--- a/luni/src/main/java/java/nio/charset/Charsets.java
+++ b/luni/src/main/java/libcore/util/CharsetUtils.java
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package java.nio.charset;
+package libcore.util;
 
 /**
  * Various special-case charset conversions (for performance).
  *
  * @hide internal use only
  */
-public final class Charsets {
+public final class CharsetUtils {
     /**
      * Returns a new byte array containing the bytes corresponding to the given characters,
      * encoded in US-ASCII. Unrepresentable characters are replaced by (byte) '?'.
@@ -75,6 +75,6 @@
      */
     public static native void isoLatin1BytesToChars(byte[] bytes, int offset, int length, char[] chars);
 
-    private Charsets() {
+    private CharsetUtils() {
     }
 }
diff --git a/luni/src/main/java/libcore/util/CountingOutputStream.java b/luni/src/main/java/libcore/util/CountingOutputStream.java
new file mode 100644
index 0000000..cc0e1f2
--- /dev/null
+++ b/luni/src/main/java/libcore/util/CountingOutputStream.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An output stream that keeps count of the number of bytes written to it.
+ *
+ * Useful when we need to make decisions based on the size of the output, such
+ * as deciding what sort of metadata to writes to zip files.
+ */
+public class CountingOutputStream extends FilterOutputStream {
+
+    private long count;
+
+    /**
+     * Constructs a new {@code FilterOutputStream} with {@code out} as its
+     * target stream.
+     *
+     * @param out the target stream that this stream writes to.
+     */
+    public CountingOutputStream(OutputStream out) {
+        super(out);
+        count = 0;
+    }
+
+    @Override
+    public void write(byte[] buffer, int offset, int length) throws IOException {
+        out.write(buffer, offset, length);
+        count += length;
+    }
+
+    @Override
+    public void write(int oneByte) throws IOException {
+        out.write(oneByte);
+        count++;
+    }
+
+    public long getCount() {
+        return count;
+    }
+}
diff --git a/luni/src/main/java/libcore/util/HexEncoding.java b/luni/src/main/java/libcore/util/HexEncoding.java
new file mode 100644
index 0000000..f883a73
--- /dev/null
+++ b/luni/src/main/java/libcore/util/HexEncoding.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.util;
+
+/**
+ * Hexadecimal encoding where each byte is represented by two hexadecimal digits.
+ */
+public class HexEncoding {
+
+    /** Hidden constructor to prevent instantiation. */
+    private HexEncoding() {}
+
+    private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    public static char[] encode(byte[] data) {
+        return encode(data, 0, data.length);
+    }
+
+    /**
+     * Encodes the provided data as a sequence of hexadecimal characters.
+     */
+    public static char[] encode(byte[] data, int offset, int len) {
+        char[] result = new char[len * 2];
+        for (int i = 0; i < len; i++) {
+            byte b = data[offset + i];
+            int resultIndex = 2 * i;
+            result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]);
+            result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]);
+        }
+
+        return result;
+    }
+
+    /**
+     * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
+     * is {@code true} odd-length inputs are allowed and the first character is interpreted
+     * as the lower bits of the first result byte.
+     *
+     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     */
+    public static byte[] decode(char[] encoded, boolean allowSingleChar) throws IllegalArgumentException {
+        int resultLengthBytes = (encoded.length + 1) / 2;
+        byte[] result = new byte[resultLengthBytes];
+
+        int resultOffset = 0;
+        int i = 0;
+        if (allowSingleChar) {
+            if ((encoded.length % 2) != 0) {
+                // Odd number of digits -- the first digit is the lower 4 bits of the first result byte.
+                result[resultOffset++] = (byte) toDigit(encoded, i);
+                i++;
+            }
+        } else {
+            if ((encoded.length % 2) != 0) {
+                throw new IllegalArgumentException("Invalid input length: " + encoded.length);
+            }
+        }
+
+        for (int len = encoded.length; i < len; i += 2) {
+            result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1));
+        }
+
+        return result;
+    }
+
+    private static int toDigit(char[] str, int offset) throws IllegalArgumentException {
+        // NOTE: that this isn't really a code point in the traditional sense, since we're
+        // just rejecting surrogate pairs outright.
+        int pseudoCodePoint = str[offset];
+
+        if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') {
+            return pseudoCodePoint - '0';
+        } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') {
+            return 10 + (pseudoCodePoint - 'a');
+        } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') {
+            return 10 + (pseudoCodePoint - 'A');
+        }
+
+        throw new IllegalArgumentException("Illegal char: " + str[offset] +
+                " at offset " + offset);
+    }
+}
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 4d58d93..329320d 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -54,7 +54,7 @@
     private final boolean mUseDst;
     private final int mDstSavings; // Implements TimeZone.getDSTSavings.
 
-    private final int[] mTransitions;
+    private final long[] mTransitions;
     private final int[] mOffsets;
     private final byte[] mTypes;
     private final byte[] mIsDsts;
@@ -76,8 +76,19 @@
 
         it.skip(4); // Skip tzh_charcnt.
 
-        int[] transitions = new int[tzh_timecnt];
-        it.readIntArray(transitions, 0, transitions.length);
+        // Transitions are signed 32 bit integers, but we store them as signed 64 bit
+        // integers since it's easier to compare them against 64 bit inputs (see getOffset
+        // and isDaylightTime) with much less risk of an overflow in our calculations.
+        //
+        // The alternative of checking the input against the first and last transition in
+        // the array is far more awkward and error prone.
+        int[] transitions32 = new int[tzh_timecnt];
+        it.readIntArray(transitions32, 0, transitions32.length);
+
+        long[] transitions64 = new long[tzh_timecnt];
+        for (int i = 0; i < tzh_timecnt; ++i) {
+            transitions64[i] = transitions32[i];
+        }
 
         byte[] type = new byte[tzh_timecnt];
         it.readByteArray(type, 0, type.length);
@@ -97,10 +108,10 @@
             it.skip(1);
         }
 
-        return new ZoneInfo(id, transitions, type, gmtOffsets, isDsts);
+        return new ZoneInfo(id, transitions64, type, gmtOffsets, isDsts);
     }
 
-    private ZoneInfo(String name, int[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) {
+    private ZoneInfo(String name, long[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) {
         mTransitions = transitions;
         mTypes = types;
         mIsDsts = isDsts;
@@ -163,7 +174,7 @@
         // no future plans (and thus no future schedule info) will report "true" from
         // useDaylightTime at the start of 2009 but "false" at the end. This seems appropriate.
         boolean usesDst = false;
-        int currentUnixTimeSeconds = (int) (System.currentTimeMillis() / 1000);
+        long currentUnixTimeSeconds = System.currentTimeMillis() / 1000;
         int i = mTransitions.length - 1;
         while (i >= 0 && mTransitions[i] >= currentUnixTimeSeconds) {
             if (mIsDsts[mTypes[i]] > 0) {
@@ -210,7 +221,7 @@
 
     @Override
     public int getOffset(long when) {
-        int unix = (int) (when / 1000);
+        long unix = when / 1000;
         int transition = Arrays.binarySearch(mTransitions, unix);
         if (transition < 0) {
             transition = ~transition - 1;
@@ -226,7 +237,7 @@
 
     @Override public boolean inDaylightTime(Date time) {
         long when = time.getTime();
-        int unix = (int) (when / 1000);
+        long unix = when / 1000;
         int transition = Arrays.binarySearch(mTransitions, unix);
         if (transition < 0) {
             transition = ~transition - 1;
@@ -952,9 +963,9 @@
      *
      * @throws CheckedArithmeticException if overflow or underflow occurs
      */
-    private static int checkedAdd(int a, int b) throws CheckedArithmeticException {
+    private static int checkedAdd(long a, int b) throws CheckedArithmeticException {
         // Adapted from Guava IntMath.checkedAdd();
-        long result = (long) a + b;
+        long result = a + b;
         if (result != (int) result) {
             throw new CheckedArithmeticException();
         }
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java
index 906ec14..b8e1dad 100644
--- a/luni/src/main/java/libcore/util/ZoneInfoDB.java
+++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java
@@ -41,7 +41,8 @@
  */
 public final class ZoneInfoDB {
   private static final TzData DATA =
-      new TzData(System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata");
+      new TzData(System.getenv("ANDROID_DATA") + "/misc/zoneinfo/current/tzdata",
+          System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata");
 
   public static class TzData {
     /**
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
index 30f4839..c81bf6b 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
@@ -63,6 +63,24 @@
     private static final ArrayList<Provider> providers = new ArrayList<Provider>(20);
 
     /**
+     * Try to load and register a provider by name from the given class-loader.
+     */
+    private static boolean initProvider(String providerClassName, ClassLoader classLoader) {
+        try {
+            Class<?> providerClass = Class.forName(providerClassName.trim(), true, classLoader);
+            Provider p = (Provider) providerClass.newInstance();
+            providers.add(p);
+            providersNames.put(p.getName(), p);
+            initServiceInfo(p);
+            return true;
+        } catch (ClassNotFoundException ignored) {
+        } catch (IllegalAccessException ignored) {
+        } catch (InstantiationException ignored) {
+        }
+        return false;
+    }
+
+    /**
      * Hash for quick provider access by name.
      */
     private static final HashMap<String, Provider> providersNames
@@ -70,18 +88,16 @@
     static {
         String providerClassName = null;
         int i = 1;
-        ClassLoader cl = ClassLoader.getSystemClassLoader();
+        ClassLoader cl = Services.class.getClassLoader();
 
         while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) {
-            try {
-                Class<?> providerClass = Class.forName(providerClassName.trim(), true, cl);
-                Provider p = (Provider) providerClass.newInstance();
-                providers.add(p);
-                providersNames.put(p.getName(), p);
-                initServiceInfo(p);
-            } catch (ClassNotFoundException ignored) {
-            } catch (IllegalAccessException ignored) {
-            } catch (InstantiationException ignored) {
+            if (!initProvider(providerClassName, cl)) {
+                // Not on the boot classpath. Try the system class-loader.
+                // Note: DO NOT USE A LOCAL FOR GETSYSTEMCLASSLOADER! This will break compile-time
+                //       initialization.
+                if (!initProvider(providerClassName, ClassLoader.getSystemClassLoader())) {
+                    // TODO: Logging?
+                }
             }
         }
         Engine.door.renumProviders();
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
index 917a3a8..020663e 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
@@ -198,8 +198,8 @@
                 }
             }
 
-            // RFC 3852 section 9.2: it authAttrs is present, it must have a
-            // message digest entry.
+            // RFC 2315 section 9.1: if authenticatedAttributes is present, it
+            // must have a message-digest attribute.
             if (existingDigest == null) {
                 throw new SecurityException("Missing MessageDigest in Authenticated Attributes");
             }
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java b/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
index 2b09309..1c07915 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
@@ -160,7 +160,7 @@
     public void verify(PublicKey key, String sigProvider) throws CertificateException,
             NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
             SignatureException {
-        verify(key, sigProvider);
+        wrapped.verify(key, sigProvider);
     }
 
     @Override
diff --git a/luni/src/main/native/IcuUtilities.cpp b/luni/src/main/native/IcuUtilities.cpp
index 7ce2168..98648a5 100644
--- a/luni/src/main/native/IcuUtilities.cpp
+++ b/luni/src/main/native/IcuUtilities.cpp
@@ -22,13 +22,12 @@
 #include "JniException.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "cutils/log.h"
 #include "unicode/strenum.h"
 #include "unicode/uloc.h"
 #include "unicode/ustring.h"
 
-jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, StringEnumeration* se) {
+jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, icu::StringEnumeration* se) {
   if (maybeThrowIcuException(env, provider, status)) {
     return NULL;
   }
@@ -40,7 +39,7 @@
 
   jobjectArray result = env->NewObjectArray(count, JniConstants::stringClass, NULL);
   for (int32_t i = 0; i < count; ++i) {
-    const UnicodeString* string = se->snext(status);
+    const icu::UnicodeString* string = se->snext(status);
     if (maybeThrowIcuException(env, "StringEnumeration::snext", status)) {
       return NULL;
     }
diff --git a/luni/src/main/native/IcuUtilities.h b/luni/src/main/native/IcuUtilities.h
index 737379e..c64de30 100644
--- a/luni/src/main/native/IcuUtilities.h
+++ b/luni/src/main/native/IcuUtilities.h
@@ -17,14 +17,11 @@
 #ifndef ICU_UTILITIES_H_included
 #define ICU_UTILITIES_H_included
 
-#undef U_HAVE_STD_STRING
-#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.
 
-extern jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, StringEnumeration*);
+extern jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, icu::StringEnumeration*);
 bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error);
 
 #endif  // ICU_UTILITIES_H_included
diff --git a/luni/src/main/native/Portability.h b/luni/src/main/native/Portability.h
index 1520311..5900cb0 100644
--- a/luni/src/main/native/Portability.h
+++ b/luni/src/main/native/Portability.h
@@ -65,7 +65,7 @@
 #include <sys/param.h>
 #include <sys/mount.h>
 
-#else  // defined(__APPLE__)
+#else
 
 // Bionic or glibc.
 
@@ -73,15 +73,33 @@
 #include <sys/sendfile.h>
 #include <sys/statvfs.h>
 
-#endif  // defined(__APPLE__)
+#endif
 
-#if !defined(__BIONIC__)
 #include <netdb.h>
-#include "../../bionic/libc/dns/include/resolv_netid.h"
-inline int android_getaddrinfofornet(const char *hostname, const char *servname,
-    const struct addrinfo *hints, unsigned /*netid*/, unsigned /*mark*/, struct addrinfo **res) {
+#if defined(__BIONIC__)
+extern "C" int android_getaddrinfofornet(const char*, const char*, const struct addrinfo*, unsigned, unsigned, struct addrinfo**);
+#else
+static inline int android_getaddrinfofornet(const char* hostname, const char* servname,
+    const struct addrinfo* hints, unsigned /*netid*/, unsigned /*mark*/, struct addrinfo** res) {
   return getaddrinfo(hostname, servname, hints, res);
 }
-#endif  // !defined(__BIONIC__)
+#endif
+
+#if defined(__GLIBC__) && !defined(__LP64__)
+
+#include <unistd.h>
+
+// 32 bit GLIBC hardcodes a "long int" as the return type for
+// TEMP_FAILURE_RETRY so the return value here gets truncated for
+// functions that return 64 bit types.
+#undef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    __typeof__(exp) _rc;                   \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
+
+#endif  // __GLIBC__ && !__LP64__
 
 #endif  // PORTABILITY_H_included
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 6a2c939..0f2d0ad 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -48,7 +48,6 @@
     REGISTER(register_java_lang_System);
     REGISTER(register_java_math_NativeBN);
     REGISTER(register_java_nio_ByteOrder);
-    REGISTER(register_java_nio_charset_Charsets);
     REGISTER(register_java_text_Bidi);
     REGISTER(register_java_util_jar_StrictJarFile);
     REGISTER(register_java_util_regex_Matcher);
@@ -58,9 +57,7 @@
     REGISTER(register_java_util_zip_Deflater);
     REGISTER(register_java_util_zip_Inflater);
     REGISTER(register_libcore_icu_AlphabeticIndex);
-    REGISTER(register_libcore_icu_DateIntervalFormat);
     REGISTER(register_libcore_icu_ICU);
-    REGISTER(register_libcore_icu_NativeBreakIterator);
     REGISTER(register_libcore_icu_NativeCollation);
     REGISTER(register_libcore_icu_NativeConverter);
     REGISTER(register_libcore_icu_NativeDecimalFormat);
@@ -72,6 +69,7 @@
     REGISTER(register_libcore_io_AsynchronousCloseMonitor);
     REGISTER(register_libcore_io_Memory);
     REGISTER(register_libcore_io_Posix);
+    REGISTER(register_libcore_util_CharsetUtils);
     REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget);
     REGISTER(register_org_apache_harmony_xml_ExpatParser);
     REGISTER(register_sun_misc_Unsafe);
diff --git a/luni/src/main/native/ZipUtilities.cpp b/luni/src/main/native/ZipUtilities.cpp
index 745b3b1..b7d2209 100644
--- a/luni/src/main/native/ZipUtilities.cpp
+++ b/luni/src/main/native/ZipUtilities.cpp
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
+#include <memory>
+
 #include "JniConstants.h"
 #include "JniException.h"
-#include "UniquePtr.h"
 #include "ZipUtilities.h"
 
 void throwExceptionForZlibError(JNIEnv* env, const char* exceptionClassName, int error,
@@ -31,7 +32,7 @@
   }
 }
 
-NativeZipStream::NativeZipStream() : input(NULL), inCap(0), mDict(NULL) {
+NativeZipStream::NativeZipStream() : inCap(0), totalIn(0), totalOut(0) {
   // Let zlib use its default allocator.
   stream.opaque = Z_NULL;
   stream.zalloc = Z_NULL;
@@ -43,7 +44,7 @@
 
 void NativeZipStream::setDictionary(JNIEnv* env, jbyteArray javaDictionary, int off, int len,
     bool inflate) {
-  UniquePtr<jbyte[]> dictionaryBytes(new jbyte[len]);
+  std::unique_ptr<jbyte[]> dictionaryBytes(new jbyte[len]);
   if (dictionaryBytes.get() == NULL) {
     jniThrowOutOfMemoryError(env, NULL);
     return;
diff --git a/luni/src/main/native/ZipUtilities.h b/luni/src/main/native/ZipUtilities.h
index fe0f977..50111a5 100644
--- a/luni/src/main/native/ZipUtilities.h
+++ b/luni/src/main/native/ZipUtilities.h
@@ -18,15 +18,18 @@
 #ifndef ZIP_UTILITIES_H_included
 #define ZIP_UTILITIES_H_included
 
-#include "UniquePtr.h"
+#include <cstdint>
+#include <memory>
 #include "jni.h"
 #include "zlib.h"
 
 class NativeZipStream {
 public:
-    UniquePtr<jbyte[]> input;
+    std::unique_ptr<jbyte[]> input;
     int inCap;
     z_stream stream;
+    uint64_t totalIn;
+    uint64_t totalOut;
 
     NativeZipStream();
     ~NativeZipStream();
@@ -34,7 +37,7 @@
     void setInput(JNIEnv* env, jbyteArray buf, jint off, jint len);
 
 private:
-    UniquePtr<jbyte[]> mDict;
+    std::unique_ptr<jbyte[]> mDict;
 
     // Disallow copy and assignment.
     NativeZipStream(const NativeZipStream&);
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 92212b9..a9031f4 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -38,6 +38,9 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <net/if_arp.h>
+#include <linux/if_ether.h>
+
 // After the others because these are not necessarily self-contained in glibc.
 #ifndef __APPLE__
 #include <linux/if_addr.h>
@@ -58,6 +61,8 @@
 static void OsConstants_initConstants(JNIEnv* env, jclass c) {
     initConstant(env, c, "AF_INET", AF_INET);
     initConstant(env, c, "AF_INET6", AF_INET6);
+    initConstant(env, c, "AF_PACKET", AF_PACKET);
+    initConstant(env, c, "AF_NETLINK", AF_NETLINK);
     initConstant(env, c, "AF_UNIX", AF_UNIX);
     initConstant(env, c, "AF_UNSPEC", AF_UNSPEC);
     initConstant(env, c, "AI_ADDRCONFIG", AI_ADDRCONFIG);
@@ -69,6 +74,8 @@
 #endif
     initConstant(env, c, "AI_PASSIVE", AI_PASSIVE);
     initConstant(env, c, "AI_V4MAPPED", AI_V4MAPPED);
+    initConstant(env, c, "ARPHRD_ETHER", ARPHRD_ETHER);
+    initConstant(env, c, "ARPHRD_LOOPBACK", ARPHRD_LOOPBACK);
 #if defined(CAP_LAST_CAP)
     initConstant(env, c, "CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL);
     initConstant(env, c, "CAP_AUDIT_WRITE", CAP_AUDIT_WRITE);
@@ -196,6 +203,9 @@
     initConstant(env, c, "ESPIPE", ESPIPE);
     initConstant(env, c, "ESRCH", ESRCH);
     initConstant(env, c, "ESTALE", ESTALE);
+    initConstant(env, c, "ETH_P_ARP", ETH_P_ARP);
+    initConstant(env, c, "ETH_P_IP", ETH_P_IP);
+    initConstant(env, c, "ETH_P_IPV6", ETH_P_IPV6);
     initConstant(env, c, "ETIME", ETIME);
     initConstant(env, c, "ETIMEDOUT", ETIMEDOUT);
     initConstant(env, c, "ETXTBSY", ETXTBSY);
@@ -355,6 +365,7 @@
     initConstant(env, c, "MS_ASYNC", MS_ASYNC);
     initConstant(env, c, "MS_INVALIDATE", MS_INVALIDATE);
     initConstant(env, c, "MS_SYNC", MS_SYNC);
+    initConstant(env, c, "NETLINK_ROUTE", NETLINK_ROUTE);
     initConstant(env, c, "NI_DGRAM", NI_DGRAM);
     initConstant(env, c, "NI_NAMEREQD", NI_NAMEREQD);
     initConstant(env, c, "NI_NOFQDN", NI_NOFQDN);
@@ -362,6 +373,7 @@
     initConstant(env, c, "NI_NUMERICSERV", NI_NUMERICSERV);
     initConstant(env, c, "O_ACCMODE", O_ACCMODE);
     initConstant(env, c, "O_APPEND", O_APPEND);
+    initConstant(env, c, "O_CLOEXEC", O_CLOEXEC);
     initConstant(env, c, "O_CREAT", O_CREAT);
     initConstant(env, c, "O_EXCL", O_EXCL);
     initConstant(env, c, "O_NOCTTY", O_NOCTTY);
@@ -406,6 +418,19 @@
     initConstant(env, c, "RT_SCOPE_NOWHERE", RT_SCOPE_NOWHERE);
     initConstant(env, c, "RT_SCOPE_SITE", RT_SCOPE_SITE);
     initConstant(env, c, "RT_SCOPE_UNIVERSE", RT_SCOPE_UNIVERSE);
+    initConstant(env, c, "RTMGRP_IPV4_IFADDR", RTMGRP_IPV4_IFADDR);
+    initConstant(env, c, "RTMGRP_IPV4_MROUTE", RTMGRP_IPV4_MROUTE);
+    initConstant(env, c, "RTMGRP_IPV4_ROUTE", RTMGRP_IPV4_ROUTE);
+    initConstant(env, c, "RTMGRP_IPV4_RULE", RTMGRP_IPV4_RULE);
+    initConstant(env, c, "RTMGRP_IPV6_IFADDR", RTMGRP_IPV6_IFADDR);
+    initConstant(env, c, "RTMGRP_IPV6_IFINFO", RTMGRP_IPV6_IFINFO);
+    initConstant(env, c, "RTMGRP_IPV6_MROUTE", RTMGRP_IPV6_MROUTE);
+    initConstant(env, c, "RTMGRP_IPV6_PREFIX", RTMGRP_IPV6_PREFIX);
+    initConstant(env, c, "RTMGRP_IPV6_ROUTE", RTMGRP_IPV6_ROUTE);
+    initConstant(env, c, "RTMGRP_LINK", RTMGRP_LINK);
+    initConstant(env, c, "RTMGRP_NEIGH", RTMGRP_NEIGH);
+    initConstant(env, c, "RTMGRP_NOTIFY", RTMGRP_NOTIFY);
+    initConstant(env, c, "RTMGRP_TC", RTMGRP_TC);
 #endif
     initConstant(env, c, "SEEK_CUR", SEEK_CUR);
     initConstant(env, c, "SEEK_END", SEEK_END);
@@ -490,6 +515,15 @@
     initConstant(env, c, "STDERR_FILENO", STDERR_FILENO);
     initConstant(env, c, "STDIN_FILENO", STDIN_FILENO);
     initConstant(env, c, "STDOUT_FILENO", STDOUT_FILENO);
+    initConstant(env, c, "ST_MANDLOCK", ST_MANDLOCK);
+    initConstant(env, c, "ST_NOATIME", ST_NOATIME);
+    initConstant(env, c, "ST_NODEV", ST_NODEV);
+    initConstant(env, c, "ST_NODIRATIME", ST_NODIRATIME);
+    initConstant(env, c, "ST_NOEXEC", ST_NOEXEC);
+    initConstant(env, c, "ST_NOSUID", ST_NOSUID);
+    initConstant(env, c, "ST_RDONLY", ST_RDONLY);
+    initConstant(env, c, "ST_RELATIME", ST_RELATIME);
+    initConstant(env, c, "ST_SYNCHRONOUS", ST_SYNCHRONOUS);
     initConstant(env, c, "S_IFBLK", S_IFBLK);
     initConstant(env, c, "S_IFCHR", S_IFCHR);
     initConstant(env, c, "S_IFDIR", S_IFDIR);
diff --git a/luni/src/main/native/java_lang_StrictMath.cpp b/luni/src/main/native/java_lang_StrictMath.cpp
index cfe375e..e8c6dfb 100644
--- a/luni/src/main/native/java_lang_StrictMath.cpp
+++ b/luni/src/main/native/java_lang_StrictMath.cpp
@@ -34,26 +34,6 @@
     return ieee_tan(a);
 }
 
-static jdouble StrictMath_asin(JNIEnv*, jclass, jdouble a) {
-    return ieee_asin(a);
-}
-
-static jdouble StrictMath_acos(JNIEnv*, jclass, jdouble a) {
-    return ieee_acos(a);
-}
-
-static jdouble StrictMath_atan(JNIEnv*, jclass, jdouble a) {
-    return ieee_atan(a);
-}
-
-static jdouble StrictMath_exp(JNIEnv*, jclass, jdouble a) {
-    return ieee_exp(a);
-}
-
-static jdouble StrictMath_log(JNIEnv*, jclass, jdouble a) {
-    return ieee_log(a);
-}
-
 static jdouble StrictMath_sqrt(JNIEnv*, jclass, jdouble a) {
     return ieee_sqrt(a);
 }
@@ -74,75 +54,30 @@
     return ieee_rint(a);
 }
 
-static jdouble StrictMath_atan2(JNIEnv*, jclass, jdouble a, jdouble b) {
-    return ieee_atan2(a, b);
-}
-
 static jdouble StrictMath_pow(JNIEnv*, jclass, jdouble a, jdouble b) {
     return ieee_pow(a,b);
 }
 
-static jdouble StrictMath_sinh(JNIEnv*, jclass, jdouble a) {
-    return ieee_sinh(a);
-}
-
-static jdouble StrictMath_tanh(JNIEnv*, jclass, jdouble a) {
-    return ieee_tanh(a);
-}
-
-static jdouble StrictMath_cosh(JNIEnv*, jclass, jdouble a) {
-    return ieee_cosh(a);
-}
-
-static jdouble StrictMath_log10(JNIEnv*, jclass, jdouble a) {
-    return ieee_log10(a);
-}
-
-static jdouble StrictMath_cbrt(JNIEnv*, jclass, jdouble a) {
-    return ieee_cbrt(a);
-}
-
-static jdouble StrictMath_expm1(JNIEnv*, jclass, jdouble a) {
-    return ieee_expm1(a);
-}
-
 static jdouble StrictMath_hypot(JNIEnv*, jclass, jdouble a, jdouble b) {
     return ieee_hypot(a, b);
 }
 
-static jdouble StrictMath_log1p(JNIEnv*, jclass, jdouble a) {
-    return ieee_log1p(a);
-}
-
 static jdouble StrictMath_nextafter(JNIEnv*, jclass, jdouble a, jdouble b) {
     return ieee_nextafter(a, b);
 }
 
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(StrictMath, IEEEremainder, "!(DD)D"),
-    NATIVE_METHOD(StrictMath, acos, "!(D)D"),
-    NATIVE_METHOD(StrictMath, asin, "!(D)D"),
-    NATIVE_METHOD(StrictMath, atan, "!(D)D"),
-    NATIVE_METHOD(StrictMath, atan2, "!(DD)D"),
-    NATIVE_METHOD(StrictMath, cbrt, "!(D)D"),
     NATIVE_METHOD(StrictMath, ceil, "!(D)D"),
     NATIVE_METHOD(StrictMath, cos, "!(D)D"),
-    NATIVE_METHOD(StrictMath, cosh, "!(D)D"),
-    NATIVE_METHOD(StrictMath, exp, "!(D)D"),
-    NATIVE_METHOD(StrictMath, expm1, "!(D)D"),
     NATIVE_METHOD(StrictMath, floor, "!(D)D"),
     NATIVE_METHOD(StrictMath, hypot, "!(DD)D"),
-    NATIVE_METHOD(StrictMath, log, "!(D)D"),
-    NATIVE_METHOD(StrictMath, log10, "!(D)D"),
-    NATIVE_METHOD(StrictMath, log1p, "!(D)D"),
     NATIVE_METHOD(StrictMath, nextafter, "!(DD)D"),
     NATIVE_METHOD(StrictMath, pow, "!(DD)D"),
     NATIVE_METHOD(StrictMath, rint, "!(D)D"),
     NATIVE_METHOD(StrictMath, sin, "!(D)D"),
-    NATIVE_METHOD(StrictMath, sinh, "!(D)D"),
     NATIVE_METHOD(StrictMath, sqrt, "!(D)D"),
     NATIVE_METHOD(StrictMath, tan, "!(D)D"),
-    NATIVE_METHOD(StrictMath, tanh, "!(D)D"),
 };
 void register_java_lang_StrictMath(JNIEnv* env) {
     jniRegisterNativeMethods(env, "java/lang/StrictMath", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/java_lang_StringToReal.cpp b/luni/src/main/native/java_lang_StringToReal.cpp
index 108f939..d1902af 100644
--- a/luni/src/main/native/java_lang_StringToReal.cpp
+++ b/luni/src/main/native/java_lang_StringToReal.cpp
@@ -25,9 +25,6 @@
 #include "cbigint.h"
 
 /* ************************* Defines ************************* */
-#if defined(__linux__) || defined(__APPLE__)
-#define USE_LL
-#endif
 
 #define LOW_I32_FROM_VAR(u64)     LOW_I32_FROM_LONG64(u64)
 #define LOW_I32_FROM_PTR(u64ptr)  LOW_I32_FROM_LONG64_PTR(u64ptr)
@@ -38,69 +35,13 @@
 
 #define DEFAULT_DOUBLE_WIDTH MAX_DOUBLE_ACCURACY_WIDTH
 
-#if defined(USE_LL)
 #define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000LL)
-#else
-#if defined(USE_L)
-#define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000L)
-#else
-#define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000)
-#endif /* USE_L */
-#endif /* USE_LL */
 
 #define DOUBLE_MINIMUM_LONGBITS (0x1)
 
-#if defined(USE_LL)
 #define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFFLL)
 #define DOUBLE_EXPONENT_MASK (0x7FF0000000000000LL)
 #define DOUBLE_NORMAL_MASK   (0x0010000000000000LL)
-#else
-#if defined(USE_L)
-#define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFFL)
-#define DOUBLE_EXPONENT_MASK (0x7FF0000000000000L)
-#define DOUBLE_NORMAL_MASK   (0x0010000000000000L)
-#else
-#define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFF)
-#define DOUBLE_EXPONENT_MASK (0x7FF0000000000000)
-#define DOUBLE_NORMAL_MASK   (0x0010000000000000)
-#endif /* USE_L */
-#endif /* USE_LL */
-
-/* Keep a count of the number of times we decrement and increment to
- * approximate the double, and attempt to detect the case where we
- * could potentially toggle back and forth between decrementing and
- * incrementing. It is possible for us to be stuck in the loop when
- * incrementing by one or decrementing by one may exceed or stay below
- * the value that we are looking for. In this case, just break out of
- * the loop if we toggle between incrementing and decrementing for more
- * than twice.
- */
-#define INCREMENT_DOUBLE(_x, _decCount, _incCount) \
-    { \
-        ++DOUBLE_TO_LONGBITS(_x); \
-        _incCount++; \
-        if( (_incCount > 2) && (_decCount > 2) ) { \
-            if( _decCount > _incCount ) { \
-                DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \
-            } else if( _incCount > _decCount ) { \
-                DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \
-            } \
-            break; \
-        } \
-    }
-#define DECREMENT_DOUBLE(_x, _decCount, _incCount) \
-    { \
-        --DOUBLE_TO_LONGBITS(_x); \
-        _decCount++; \
-        if( (_incCount > 2) && (_decCount > 2) ) { \
-            if( _decCount > _incCount ) { \
-                DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \
-            } else if( _incCount > _decCount ) { \
-                DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \
-            } \
-            break; \
-        } \
-    }
 
 #define allocateU64(x, n) if (!((x) = reinterpret_cast<uint64_t*>(malloc((n) * sizeof(uint64_t))))) goto OutOfMemory;
 
@@ -248,7 +189,6 @@
     }
 
   return result;
-
 }
 
 static jdouble createDouble1(JNIEnv* env, uint64_t* f, int32_t length, jint e) {
@@ -310,7 +250,6 @@
      first and let it fall to zero if need be. */
 
   if (result == 0.0)
-
     DOUBLE_TO_LONGBITS (result) = DOUBLE_MINIMUM_LONGBITS;
 
   return doubleAlgorithm (env, f, length, e, result);
@@ -323,15 +262,6 @@
  *      Clinger, Proceedings of the ACM SIGPLAN '90 Conference on
  *      Programming Language Design and Implementation, June 20-22,
  *      1990, pp. 92-101.
- *
- * There is a possibility that the function will end up in an endless
- * loop if the given approximating floating-point number (a very small
- * floating-point whose value is very close to zero) straddles between
- * two approximating integer values. We modified the algorithm slightly
- * to detect the case where it oscillates back and forth between
- * incrementing and decrementing the floating-point approximation. It
- * is currently set such that if the oscillation occurs more than twice
- * then return the original approximation.
  */
 static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, jdouble z) {
   uint64_t m;
@@ -340,11 +270,10 @@
   uint64_t* y;
   uint64_t* D;
   uint64_t* D2;
-  int32_t xLength, yLength, DLength, D2Length, decApproxCount, incApproxCount;
+  int32_t xLength, yLength, DLength, D2Length;
 
   x = y = D = D2 = 0;
   xLength = yLength = DLength = D2Length = 0;
-  decApproxCount = incApproxCount = 0;
 
   do
     {
@@ -443,12 +372,13 @@
       comparison2 = compareHighPrecision (D2, D2Length, y, yLength);
       if (comparison2 < 0)
         {
-          if (comparison < 0 && m == DOUBLE_NORMAL_MASK)
+          if (comparison < 0 && m == DOUBLE_NORMAL_MASK
+              && DOUBLE_TO_LONGBITS(z) != DOUBLE_NORMAL_MASK)
             {
               simpleShiftLeftHighPrecision (D2, D2Length, 1);
               if (compareHighPrecision (D2, D2Length, y, yLength) > 0)
                 {
-                  DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+                  --DOUBLE_TO_LONGBITS (z);
                 }
               else
                 {
@@ -466,7 +396,7 @@
             {
               if (comparison < 0 && m == DOUBLE_NORMAL_MASK)
                 {
-                  DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+                  --DOUBLE_TO_LONGBITS (z);
                 }
               else
                 {
@@ -475,24 +405,24 @@
             }
           else if (comparison < 0)
             {
-              DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+              --DOUBLE_TO_LONGBITS (z);
               break;
             }
           else
             {
-              INCREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+              ++DOUBLE_TO_LONGBITS (z);
               break;
             }
         }
       else if (comparison < 0)
         {
-          DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+          --DOUBLE_TO_LONGBITS (z);
         }
       else
         {
           if (DOUBLE_TO_LONGBITS (z) == DOUBLE_INFINITE_LONGBITS)
             break;
-          INCREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+          ++DOUBLE_TO_LONGBITS (z);
         }
     }
   while (1);
@@ -547,42 +477,6 @@
 #define FLOAT_EXPONENT_MASK (0x7F800000)
 #define FLOAT_NORMAL_MASK   (0x00800000)
 
-/* Keep a count of the number of times we decrement and increment to
- * approximate the double, and attempt to detect the case where we
- * could potentially toggle back and forth between decrementing and
- * incrementing. It is possible for us to be stuck in the loop when
- * incrementing by one or decrementing by one may exceed or stay below
- * the value that we are looking for. In this case, just break out of
- * the loop if we toggle between incrementing and decrementing for more
- * than twice.
- */
-#define INCREMENT_FLOAT(_x, _decCount, _incCount) \
-    { \
-        ++FLOAT_TO_INTBITS(_x); \
-        _incCount++; \
-        if( (_incCount > 2) && (_decCount > 2) ) { \
-            if( _decCount > _incCount ) { \
-                FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \
-            } else if( _incCount > _decCount ) { \
-                FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \
-            } \
-            break; \
-        } \
-    }
-#define DECREMENT_FLOAT(_x, _decCount, _incCount) \
-    { \
-        --FLOAT_TO_INTBITS(_x); \
-        _decCount++; \
-        if( (_incCount > 2) && (_decCount > 2) ) { \
-            if( _decCount > _incCount ) { \
-                FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \
-            } else if( _incCount > _decCount ) { \
-                FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \
-            } \
-            break; \
-        } \
-    }
-
 static jfloat createFloat(JNIEnv* env, const char* s, jint e) {
   /* assumes s is a null terminated string with at least one
    * character in it */
@@ -682,7 +576,6 @@
     }
 
   return result;
-
 }
 
 static jfloat createFloat1 (JNIEnv* env, uint64_t* f, int32_t length, jint e) {
@@ -796,15 +689,6 @@
  *      Clinger, Proceedings of the ACM SIGPLAN '90 Conference on
  *      Programming Language Design and Implementation, June 20-22,
  *      1990, pp. 92-101.
- *
- * There is a possibility that the function will end up in an endless
- * loop if the given approximating floating-point number (a very small
- * floating-point whose value is very close to zero) straddles between
- * two approximating integer values. We modified the algorithm slightly
- * to detect the case where it oscillates back and forth between
- * incrementing and decrementing the floating-point approximation. It
- * is currently set such that if the oscillation occurs more than twice
- * then return the original approximation.
  */
 static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, jfloat z) {
   uint64_t m;
@@ -814,11 +698,9 @@
   uint64_t* D;
   uint64_t* D2;
   int32_t xLength, yLength, DLength, D2Length;
-  int32_t decApproxCount, incApproxCount;
 
   x = y = D = D2 = 0;
   xLength = yLength = DLength = D2Length = 0;
-  decApproxCount = incApproxCount = 0;
 
   do
     {
@@ -917,12 +799,13 @@
       comparison2 = compareHighPrecision (D2, D2Length, y, yLength);
       if (comparison2 < 0)
         {
-          if (comparison < 0 && m == FLOAT_NORMAL_MASK)
+          if (comparison < 0 && m == FLOAT_NORMAL_MASK
+              && FLOAT_TO_INTBITS(z) != FLOAT_NORMAL_MASK)
             {
               simpleShiftLeftHighPrecision (D2, D2Length, 1);
               if (compareHighPrecision (D2, D2Length, y, yLength) > 0)
                 {
-                  DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+                  --FLOAT_TO_INTBITS (z);
                 }
               else
                 {
@@ -940,7 +823,7 @@
             {
               if (comparison < 0 && m == FLOAT_NORMAL_MASK)
                 {
-                  DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+                  --FLOAT_TO_INTBITS (z);
                 }
               else
                 {
@@ -949,24 +832,24 @@
             }
           else if (comparison < 0)
             {
-              DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+              --FLOAT_TO_INTBITS (z);
               break;
             }
           else
             {
-              INCREMENT_FLOAT (z, decApproxCount, incApproxCount);
+              ++FLOAT_TO_INTBITS (z);
               break;
             }
         }
       else if (comparison < 0)
         {
-          DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+          --FLOAT_TO_INTBITS (z);
         }
       else
         {
           if (FLOAT_TO_INTBITS (z) == FLOAT_EXPONENT_MASK)
             break;
-          INCREMENT_FLOAT (z, decApproxCount, incApproxCount);
+          ++FLOAT_TO_INTBITS (z);
         }
     }
   while (1);
diff --git a/luni/src/main/native/java_lang_System.cpp b/luni/src/main/native/java_lang_System.cpp
index 944c0c3..306adab 100644
--- a/luni/src/main/native/java_lang_System.cpp
+++ b/luni/src/main/native/java_lang_System.cpp
@@ -84,7 +84,11 @@
     properties.push_back(std::string("user.dir=") + getcwd(path, sizeof(path)));
 
     properties.push_back("android.zlib.version=" ZLIB_VERSION);
+#if defined(OPENSSL_IS_BORINGSSL)
+    properties.push_back("android.openssl.version=BoringSSL");
+#else
     properties.push_back("android.openssl.version=" OPENSSL_VERSION_TEXT);
+#endif
 
     const char* library_path = getenv("LD_LIBRARY_PATH");
 #if defined(HAVE_ANDROID_OS)
@@ -109,33 +113,20 @@
 }
 
 static jlong System_nanoTime(JNIEnv*, jclass) {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
     timespec now;
     clock_gettime(CLOCK_MONOTONIC, &now);
     return now.tv_sec * 1000000000LL + now.tv_nsec;
-#else
+#else // __APPLE__
     timeval now;
     gettimeofday(&now, NULL);
     return static_cast<jlong>(now.tv_sec) * 1000000000LL + now.tv_usec * 1000LL;
 #endif
 }
 
-static jstring System_mapLibraryName(JNIEnv* env, jclass, jstring javaName) {
-    ScopedUtfChars name(env, javaName);
-    if (name.c_str() == NULL) {
-        return NULL;
-    }
-    char* mappedName = NULL;
-    asprintf(&mappedName, OS_SHARED_LIB_FORMAT_STR, name.c_str());
-    jstring result = env->NewStringUTF(mappedName);
-    free(mappedName);
-    return result;
-}
-
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(System, currentTimeMillis, "!()J"),
     NATIVE_METHOD(System, log, "(CLjava/lang/String;Ljava/lang/Throwable;)V"),
-    NATIVE_METHOD(System, mapLibraryName, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(System, nanoTime, "!()J"),
     NATIVE_METHOD(System, setFieldImpl, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"),
     NATIVE_METHOD(System, specialProperties, "()[Ljava/lang/String;"),
diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp
index be87ea6..7e77e23 100644
--- a/luni/src/main/native/java_math_NativeBN.cpp
+++ b/luni/src/main/native/java_math_NativeBN.cpp
@@ -21,19 +21,31 @@
 #include "JniException.h"
 #include "ScopedPrimitiveArray.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "jni.h"
 #include <openssl/bn.h>
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <stdio.h>
+#include <memory>
+
+#if defined(OPENSSL_IS_BORINGSSL)
+/* BoringSSL no longer exports |bn_check_top|. */
+static void bn_check_top(const BIGNUM* bn) {
+  /* This asserts that |bn->top| (which contains the number of elements of
+   * |bn->d| that are valid) is minimal. In other words, that there aren't
+   * superfluous zeros. */
+  if (bn != NULL && bn->top != 0 && bn->d[bn->top-1] == 0) {
+    abort();
+  }
+}
+#endif
 
 struct BN_CTX_Deleter {
   void operator()(BN_CTX* p) const {
     BN_CTX_free(p);
   }
 };
-typedef UniquePtr<BN_CTX, BN_CTX_Deleter> Unique_BN_CTX;
+typedef std::unique_ptr<BN_CTX, BN_CTX_Deleter> Unique_BN_CTX;
 
 static BIGNUM* toBigNum(jlong address) {
   return reinterpret_cast<BIGNUM*>(static_cast<uintptr_t>(address));
@@ -109,28 +121,32 @@
 }
 
 static void NativeBN_putULongInt(JNIEnv* env, jclass, jlong a0, jlong java_dw, jboolean neg) {
-    if (!oneValidHandle(env, a0)) return;
+  if (!oneValidHandle(env, a0)) return;
 
-    uint64_t dw = java_dw;
+  uint64_t dw = java_dw;
+  BIGNUM* a = toBigNum(a0);
+  int ok;
 
-    // cf. litEndInts2bn:
-    BIGNUM* a = toBigNum(a0);
-    bn_check_top(a);
-    if (bn_wexpand(a, 8/BN_BYTES) != NULL) {
-#ifdef __LP64__
+  static_assert(sizeof(dw) == sizeof(BN_ULONG) ||
+                sizeof(dw) == 2*sizeof(BN_ULONG), "Unknown BN configuration");
+
+  if (sizeof(dw) == sizeof(BN_ULONG)) {
+    ok = BN_set_word(a, dw);
+  } else if (sizeof(dw) == 2 * sizeof(BN_ULONG)) {
+    ok = (bn_wexpand(a, 2) != NULL);
+    if (ok) {
       a->d[0] = dw;
-#else
-      unsigned int hi = dw >> 32; // This shifts without sign extension.
-      int lo = (int)dw; // This truncates implicitly.
-      a->d[0] = lo;
-      a->d[1] = hi;
-#endif
-      a->top = 8 / BN_BYTES;
-      a->neg = neg;
+      a->d[1] = dw >> 32;
+      a->top = 2;
       bn_correct_top(a);
-    } else {
-      throwExceptionIfNecessary(env);
     }
+  }
+
+  BN_set_negative(a, neg);
+
+  if (!ok) {
+    throwExceptionIfNecessary(env);
+  }
 }
 
 static void NativeBN_putLongInt(JNIEnv* env, jclass cls, jlong a, jlong dw) {
@@ -240,25 +256,25 @@
 
   bn_check_top(ret);
   // FIXME: assert bytesLen > 0
-  int wLen = (bytesLen + BN_BYTES - 1) / BN_BYTES;
+  int wLen = (bytesLen + sizeof(BN_ULONG) - 1) / sizeof(BN_ULONG);
   int firstNonzeroDigit = -2;
   if (bn_wexpand(ret, wLen) != NULL) {
     BN_ULONG* d = ret->d;
     BN_ULONG di;
     ret->top = wLen;
-    int highBytes = bytesLen % BN_BYTES;
+    int highBytes = bytesLen % sizeof(BN_ULONG);
     int k = bytesLen;
     // Put bytes to the int array starting from the end of the byte array
     int i = 0;
     while (k > highBytes) {
-      k -= BN_BYTES;
+      k -= sizeof(BN_ULONG);
       di = BYTES2ULONG(bytes, k);
       if (di != 0) {
         d[i] = -di;
         firstNonzeroDigit = i;
         i++;
         while (k > highBytes) {
-          k -= BN_BYTES;
+          k -= sizeof(BN_ULONG);
           d[i] = ~BYTES2ULONG(bytes, k);
           i++;
         }
@@ -394,7 +410,7 @@
   if (wLen == 0) {
     return NULL;
   }
-  jintArray result = env->NewIntArray(wLen * BN_BYTES/sizeof(unsigned int));
+  jintArray result = env->NewIntArray(wLen * sizeof(BN_ULONG)/sizeof(unsigned int));
   if (result == NULL) {
     return NULL;
   }
@@ -445,7 +461,7 @@
     do { i--; } while (!((i < 0) || (d[i] != 0)));
     if (i < 0) msd--; // Only if all lower significant digits are 0 we decrement the most significant one.
   }
-  return (wLen - 1) * BN_BYTES * 8 + BN_num_bits_word(msd);
+  return (wLen - 1) * sizeof(BN_ULONG) * 8 + BN_num_bits_word(msd);
 }
 
 static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass, jlong a, int n) {
diff --git a/luni/src/main/native/java_text_Bidi.cpp b/luni/src/main/native/java_text_Bidi.cpp
index d9ef35d..6a3e751 100644
--- a/luni/src/main/native/java_text_Bidi.cpp
+++ b/luni/src/main/native/java_text_Bidi.cpp
@@ -22,14 +22,14 @@
 #include "JniConstants.h"
 #include "JniException.h"
 #include "ScopedPrimitiveArray.h"
-#include "UniquePtr.h"
 #include "unicode/ubidi.h"
 
 #include <stdlib.h>
 #include <string.h>
+#include <memory>
 
 struct BiDiData {
-    BiDiData(UBiDi* biDi) : mBiDi(biDi), mEmbeddingLevels(NULL) {
+    BiDiData(UBiDi* biDi) : mBiDi(biDi) {
     }
 
     ~BiDiData() {
@@ -50,7 +50,7 @@
 
 private:
     UBiDi* mBiDi;
-    UniquePtr<jbyte[]> mEmbeddingLevels;
+    std::unique_ptr<jbyte[]> mEmbeddingLevels;
 
     // Disallow copy and assignment.
     BiDiData(const BiDiData&);
@@ -98,7 +98,7 @@
     if (maybeThrowIcuException(env, "ubidi_openSized", status)) {
         return 0;
     }
-    UniquePtr<BiDiData> lineData(new BiDiData(sized));
+    std::unique_ptr<BiDiData> lineData(new BiDiData(sized));
     ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status);
     maybeThrowIcuException(env, "ubidi_setLine", status);
     return reinterpret_cast<uintptr_t>(lineData.release());
@@ -168,7 +168,7 @@
 
     const UBiDiLevel* levels = reinterpret_cast<const UBiDiLevel*>(levelBytes.get());
 
-    UniquePtr<int[]> indexMap(new int[length]);
+    std::unique_ptr<int[]> indexMap(new int[length]);
     ubidi_reorderVisual(levels, length, &indexMap[0]);
 
     jintArray result = env->NewIntArray(length);
diff --git a/luni/src/main/native/java_util_jar_StrictJarFile.cpp b/luni/src/main/native/java_util_jar_StrictJarFile.cpp
index efcc74c..82547bd 100644
--- a/luni/src/main/native/java_util_jar_StrictJarFile.cpp
+++ b/luni/src/main/native/java_util_jar_StrictJarFile.cpp
@@ -17,49 +17,26 @@
 
 #define LOG_TAG "StrictJarFile"
 
+#include <memory>
 #include <string>
 
 #include "JNIHelp.h"
 #include "JniConstants.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "jni.h"
 #include "ziparchive/zip_archive.h"
 #include "cutils/log.h"
 
+// The method ID for ZipEntry.<init>(String,String,JJJIII[BJJ)
+static jmethodID zipEntryCtor;
+
 static void throwIoException(JNIEnv* env, const int32_t errorCode) {
   jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode));
 }
 
-// Constructs a string out of |name| with the default charset (UTF-8 on android).
-// We prefer this to JNI's NewStringUTF because the string constructor will
-// replace unmappable and malformed bytes instead of throwing. See b/18584205
-//
-// Returns |NULL| iff. we couldn't allocate the string object or its constructor
-// arguments.
-//
-// TODO: switch back to NewStringUTF after libziparchive is modified to reject
-// files whose names aren't valid UTF-8.
-static jobject constructString(JNIEnv* env, const char* name, const uint16_t nameLength) {
-  jbyteArray javaNameBytes = env->NewByteArray(nameLength);
-  if (javaNameBytes == NULL) {
-      return NULL;
-  }
-  env->SetByteArrayRegion(javaNameBytes, 0, nameLength, reinterpret_cast<const jbyte*>(name));
-
-  ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
-  const jmethodID stringCtor = env->GetMethodID(stringClass.get(), "<init>", "([B)V");
-  return env->NewObject(stringClass.get(), stringCtor, javaNameBytes);
-}
-
-static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, const jobject entryName,
-                           const uint16_t nameLength) {
-  ScopedLocalRef<jclass> zipEntryClass(env, env->FindClass("java/util/zip/ZipEntry"));
-  const jmethodID zipEntryCtor = env->GetMethodID(zipEntryClass.get(), "<init>",
-                                   "(Ljava/lang/String;Ljava/lang/String;JJJIII[BIJJ)V");
-
-  return env->NewObject(zipEntryClass.get(),
+static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
+  return env->NewObject(JniConstants::zipEntryClass,
                         zipEntryCtor,
                         entryName,
                         NULL,  // comment
@@ -70,16 +47,10 @@
                         static_cast<jint>(0),  // time
                         static_cast<jint>(0),  // modData
                         NULL,  // byte[] extra
-                        static_cast<jint>(nameLength),
                         static_cast<jlong>(-1),  // local header offset
                         static_cast<jlong>(entry.offset));
 }
 
-static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, const char* name,
-                           const uint16_t nameLength) {
-  return newZipEntry(env, entry, constructString(env, name, nameLength), nameLength);
-}
-
 static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) {
   ScopedUtfChars fileChars(env, fileName);
   if (fileChars.c_str() == NULL) {
@@ -98,25 +69,20 @@
 
 class IterationHandle {
  public:
-  IterationHandle(const char* prefix) :
-    cookie_(NULL), prefix_(strdup(prefix)) {
+  IterationHandle() :
+    cookie_(NULL) {
   }
 
   void** CookieAddress() {
     return &cookie_;
   }
 
-  const char* Prefix() const {
-    return prefix_;
-  }
-
   ~IterationHandle() {
-    free(prefix_);
+    EndIteration(cookie_);
   }
 
  private:
   void* cookie_;
-  char* prefix_;
 };
 
 
@@ -127,14 +93,15 @@
     return static_cast<jlong>(-1);
   }
 
-  IterationHandle* handle = new IterationHandle(prefixChars.c_str());
+  IterationHandle* handle = new IterationHandle();
   int32_t error = 0;
   if (prefixChars.size() == 0) {
     error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
                            handle->CookieAddress(), NULL);
   } else {
+    ZipEntryName entry_name(prefixChars.c_str());
     error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
-                           handle->CookieAddress(), handle->Prefix());
+                           handle->CookieAddress(), &entry_name);
   }
 
   if (error) {
@@ -156,11 +123,12 @@
     return NULL;
   }
 
-  UniquePtr<char[]> entryNameCString(new char[entryName.name_length + 1]);
+  std::unique_ptr<char[]> entryNameCString(new char[entryName.name_length + 1]);
   memcpy(entryNameCString.get(), entryName.name, entryName.name_length);
   entryNameCString[entryName.name_length] = '\0';
+  ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryNameCString.get()));
 
-  return newZipEntry(env, data, entryNameCString.get(), entryName.name_length);
+  return newZipEntry(env, data, entryNameString.get());
 }
 
 static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
@@ -172,12 +140,12 @@
 
   ZipEntry data;
   const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
-                                  entryNameChars.c_str(), &data);
+                                  ZipEntryName(entryNameChars.c_str()), &data);
   if (error) {
     return NULL;
   }
 
-  return newZipEntry(env, data, entryName, entryNameChars.size());
+  return newZipEntry(env, data, entryName);
 }
 
 static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
@@ -194,4 +162,8 @@
 
 void register_java_util_jar_StrictJarFile(JNIEnv* env) {
   jniRegisterNativeMethods(env, "java/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
+
+  zipEntryCtor = env->GetMethodID(JniConstants::zipEntryClass, "<init>",
+      "(Ljava/lang/String;Ljava/lang/String;JJJIII[BJJ)V");
+  LOG_ALWAYS_FATAL_IF(zipEntryCtor == NULL, "Unable to find ZipEntry.<init>");
 }
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
index 2e5259e..35d014c 100644
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ b/luni/src/main/native/java_util_regex_Matcher.cpp
@@ -23,15 +23,14 @@
 #include "JniConstants.h"
 #include "JniException.h"
 #include "ScopedPrimitiveArray.h"
-#include "UniquePtr.h"
 #include "jni.h"
 #include "unicode/parseerr.h"
 #include "unicode/regex.h"
 
 // ICU documentation: http://icu-project.org/apiref/icu4c/classRegexMatcher.html
 
-static RegexMatcher* toRegexMatcher(jlong address) {
-    return reinterpret_cast<RegexMatcher*>(static_cast<uintptr_t>(address));
+static icu::RegexMatcher* toRegexMatcher(jlong address) {
+    return reinterpret_cast<icu::RegexMatcher*>(static_cast<uintptr_t>(address));
 }
 
 /**
@@ -75,7 +74,7 @@
         maybeThrowIcuException(mEnv, "utext_close", mStatus);
     }
 
-    RegexMatcher* operator->() {
+    icu::RegexMatcher* operator->() {
         return mMatcher;
     }
 
@@ -107,7 +106,7 @@
 
     JNIEnv* mEnv;
     jstring mJavaInput;
-    RegexMatcher* mMatcher;
+    icu::RegexMatcher* mMatcher;
     const jchar* mChars;
     UErrorCode mStatus;
     UText* mUText;
@@ -171,9 +170,9 @@
 }
 
 static jlong Matcher_openImpl(JNIEnv* env, jclass, jlong patternAddr) {
-    RegexPattern* pattern = reinterpret_cast<RegexPattern*>(static_cast<uintptr_t>(patternAddr));
+    icu::RegexPattern* pattern = reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(patternAddr));
     UErrorCode status = U_ZERO_ERROR;
-    RegexMatcher* result = pattern->matcher(status);
+    icu::RegexMatcher* result = pattern->matcher(status);
     maybeThrowIcuException(env, "RegexPattern::matcher", status);
     return reinterpret_cast<uintptr_t>(result);
 }
diff --git a/luni/src/main/native/java_util_regex_Pattern.cpp b/luni/src/main/native/java_util_regex_Pattern.cpp
index 1a99d0a..f2c07dc 100644
--- a/luni/src/main/native/java_util_regex_Pattern.cpp
+++ b/luni/src/main/native/java_util_regex_Pattern.cpp
@@ -27,8 +27,8 @@
 
 // ICU documentation: http://icu-project.org/apiref/icu4c/classRegexPattern.html
 
-static RegexPattern* toRegexPattern(jlong addr) {
-    return reinterpret_cast<RegexPattern*>(static_cast<uintptr_t>(addr));
+static icu::RegexPattern* toRegexPattern(jlong addr) {
+    return reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(addr));
 }
 
 static const char* regexDetailMessage(UErrorCode status) {
@@ -86,8 +86,8 @@
     if (!regex.valid()) {
         return 0;
     }
-    UnicodeString& regexString(regex.unicodeString());
-    RegexPattern* result = RegexPattern::compile(regexString, flags, error, status);
+    icu::UnicodeString& regexString(regex.unicodeString());
+    icu::RegexPattern* result = icu::RegexPattern::compile(regexString, flags, error, status);
     if (!U_SUCCESS(status)) {
         throwPatternSyntaxException(env, status, javaRegex, error);
     }
diff --git a/luni/src/main/native/java_util_zip_Deflater.cpp b/luni/src/main/native/java_util_zip_Deflater.cpp
index 1afd36e..d963824 100644
--- a/luni/src/main/native/java_util_zip_Deflater.cpp
+++ b/luni/src/main/native/java_util_zip_Deflater.cpp
@@ -28,11 +28,11 @@
 }
 
 static jlong Deflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
-    return toNativeZipStream(handle)->stream.total_in;
+    return toNativeZipStream(handle)->totalIn;
 }
 
 static jlong Deflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
-    return toNativeZipStream(handle)->stream.total_out;
+    return toNativeZipStream(handle)->totalOut;
 }
 
 static jint Deflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
@@ -40,7 +40,7 @@
 }
 
 static jlong Deflater_createStream(JNIEnv * env, jobject, jint level, jint strategy, jboolean noHeader) {
-    UniquePtr<NativeZipStream> jstream(new NativeZipStream);
+    std::unique_ptr<NativeZipStream> jstream(new NativeZipStream);
     if (jstream.get() == NULL) {
         jniThrowOutOfMemoryError(env, NULL);
         return -1;
@@ -101,6 +101,9 @@
     jint bytesRead = stream->stream.next_in - initialNextIn;
     jint bytesWritten = stream->stream.next_out - initialNextOut;
 
+    stream->totalIn += bytesRead;
+    stream->totalOut += bytesWritten;
+
     static jfieldID inReadField = env->GetFieldID(JniConstants::deflaterClass, "inRead", "I");
     jint inReadValue = env->GetIntField(recv, inReadField);
     inReadValue += bytesRead;
@@ -116,6 +119,8 @@
 
 static void Deflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
     NativeZipStream* stream = toNativeZipStream(handle);
+    stream->totalIn = 0;
+    stream->totalOut = 0;
     int err = deflateReset(&stream->stream);
     if (err != Z_OK) {
         throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", 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 ca3ee09..f0878ff 100644
--- a/luni/src/main/native/java_util_zip_Inflater.cpp
+++ b/luni/src/main/native/java_util_zip_Inflater.cpp
@@ -25,7 +25,7 @@
 #include <errno.h>
 
 static jlong Inflater_createStream(JNIEnv* env, jobject, jboolean noHeader) {
-    UniquePtr<NativeZipStream> jstream(new NativeZipStream);
+    std::unique_ptr<NativeZipStream> jstream(new NativeZipStream);
     if (jstream.get() == NULL) {
         jniThrowOutOfMemoryError(env, NULL);
         return -1;
@@ -122,6 +122,9 @@
     jint bytesRead = stream->stream.next_in - initialNextIn;
     jint bytesWritten = stream->stream.next_out - initialNextOut;
 
+    stream->totalIn += bytesRead;
+    stream->totalOut += bytesWritten;
+
     static jfieldID inReadField = env->GetFieldID(JniConstants::inflaterClass, "inRead", "I");
     jint inReadValue = env->GetIntField(recv, inReadField);
     inReadValue += bytesRead;
@@ -145,6 +148,8 @@
 
 static void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
     NativeZipStream* stream = toNativeZipStream(handle);
+    stream->totalIn = 0;
+    stream->totalOut = 0;
     int err = inflateReset(&stream->stream);
     if (err != Z_OK) {
         throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream);
@@ -152,11 +157,11 @@
 }
 
 static jlong Inflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
-    return toNativeZipStream(handle)->stream.total_out;
+    return toNativeZipStream(handle)->totalOut;
 }
 
 static jlong Inflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
-    return toNativeZipStream(handle)->stream.total_in;
+    return toNativeZipStream(handle)->totalIn;
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp b/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp
index e0638bd..acc247b 100644
--- a/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp
+++ b/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp
@@ -25,8 +25,8 @@
 #include "unicode/alphaindex.h"
 #include "unicode/uniset.h"
 
-static AlphabeticIndex* fromPeer(jlong peer) {
-  return reinterpret_cast<AlphabeticIndex*>(static_cast<uintptr_t>(peer));
+static icu::AlphabeticIndex* fromPeer(jlong peer) {
+  return reinterpret_cast<icu::AlphabeticIndex*>(static_cast<uintptr_t>(peer));
 }
 
 static jlong AlphabeticIndex_create(JNIEnv* env, jclass, jstring javaLocaleName) {
@@ -35,7 +35,7 @@
   if (!icuLocale.valid()) {
     return 0;
   }
-  AlphabeticIndex* ai = new AlphabeticIndex(icuLocale.locale(), status);
+  icu::AlphabeticIndex* ai = new icu::AlphabeticIndex(icuLocale.locale(), status);
   if (maybeThrowIcuException(env, "AlphabeticIndex", status)) {
     return 0;
   }
@@ -47,19 +47,19 @@
 }
 
 static jint AlphabeticIndex_getMaxLabelCount(JNIEnv*, jclass, jlong peer) {
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   return ai->getMaxLabelCount();
 }
 
 static void AlphabeticIndex_setMaxLabelCount(JNIEnv* env, jclass, jlong peer, jint count) {
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   UErrorCode status = U_ZERO_ERROR;
   ai->setMaxLabelCount(count, status);
   maybeThrowIcuException(env, "AlphabeticIndex::setMaxLabelCount", status);
 }
 
 static void AlphabeticIndex_addLabels(JNIEnv* env, jclass, jlong peer, jstring javaLocaleName) {
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   ScopedIcuLocale icuLocale(env, javaLocaleName);
   if (!icuLocale.valid()) {
     return;
@@ -71,14 +71,14 @@
 
 static void AlphabeticIndex_addLabelRange(JNIEnv* env, jclass, jlong peer,
                                           jint codePointStart, jint codePointEnd) {
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   UErrorCode status = U_ZERO_ERROR;
-  ai->addLabels(UnicodeSet(codePointStart, codePointEnd), status);
+  ai->addLabels(icu::UnicodeSet(codePointStart, codePointEnd), status);
   maybeThrowIcuException(env, "AlphabeticIndex::addLabels", status);
 }
 
 static jint AlphabeticIndex_getBucketCount(JNIEnv* env, jclass, jlong peer) {
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   UErrorCode status = U_ZERO_ERROR;
   jint result = ai->getBucketCount(status);
   if (maybeThrowIcuException(env, "AlphabeticIndex::getBucketCount", status)) {
@@ -88,7 +88,7 @@
 }
 
 static jint AlphabeticIndex_getBucketIndex(JNIEnv* env, jclass, jlong peer, jstring javaString) {
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   ScopedJavaUnicodeString string(env, javaString);
   if (!string.valid()) {
     return -1;
@@ -108,7 +108,7 @@
   }
 
   // Iterate to the nth bucket.
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   UErrorCode status = U_ZERO_ERROR;
   ai->resetBucketIterator(status);
   if (maybeThrowIcuException(env, "AlphabeticIndex::resetBucketIterator", status)) {
@@ -129,31 +129,31 @@
     return env->NewStringUTF("");
   }
 
-  const UnicodeString& label(ai->getBucketLabel());
+  const icu::UnicodeString& label(ai->getBucketLabel());
   return env->NewString(label.getBuffer(), label.length());
 }
 
 static jlong AlphabeticIndex_buildImmutableIndex(JNIEnv* env, jclass, jlong peer) {
-  AlphabeticIndex* ai = fromPeer(peer);
+  icu::AlphabeticIndex* ai = fromPeer(peer);
   UErrorCode status = U_ZERO_ERROR;
-  AlphabeticIndex::ImmutableIndex* ii = ai->buildImmutableIndex(status);
+  icu::AlphabeticIndex::ImmutableIndex* ii = ai->buildImmutableIndex(status);
   if (maybeThrowIcuException(env, "AlphabeticIndex::buildImmutableIndex", status)) {
     return 0;
   }
   return reinterpret_cast<uintptr_t>(ii);
 }
 
-static AlphabeticIndex::ImmutableIndex* immutableIndexFromPeer(jlong peer) {
-  return reinterpret_cast<AlphabeticIndex::ImmutableIndex*>(static_cast<uintptr_t>(peer));
+static icu::AlphabeticIndex::ImmutableIndex* immutableIndexFromPeer(jlong peer) {
+  return reinterpret_cast<icu::AlphabeticIndex::ImmutableIndex*>(static_cast<uintptr_t>(peer));
 }
 
 static jint ImmutableIndex_getBucketCount(JNIEnv*, jclass, jlong peer) {
-  AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
+  icu::AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
   return ii->getBucketCount();
 }
 
 static jint ImmutableIndex_getBucketIndex(JNIEnv* env, jclass, jlong peer, jstring javaString) {
-  AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
+  icu::AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
   ScopedJavaUnicodeString string(env, javaString);
   if (!string.valid()) {
     return -1;
@@ -167,8 +167,8 @@
 }
 
 static jstring ImmutableIndex_getBucketLabel(JNIEnv* env, jclass, jlong peer, jint index) {
-  AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
-  const AlphabeticIndex::Bucket* bucket = ii->getBucket(index);
+  icu::AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
+  const icu::AlphabeticIndex::Bucket* bucket = ii->getBucket(index);
   if (bucket == NULL) {
     jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid index: %d", index);
     return NULL;
@@ -179,7 +179,7 @@
     return env->NewStringUTF("");
   }
 
-  const UnicodeString& label(bucket->getLabel());
+  const icu::UnicodeString& label(bucket->getLabel());
   return env->NewString(label.getBuffer(), label.length());
 }
 
diff --git a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
deleted file mode 100644
index a3258c1..0000000
--- a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "DateIntervalFormat"
-
-#include "IcuUtilities.h"
-#include "JniConstants.h"
-#include "ScopedIcuLocale.h"
-#include "ScopedJavaUnicodeString.h"
-#include "UniquePtr.h"
-#include "cutils/log.h"
-#include "unicode/dtitvfmt.h"
-
-static jlong DateIntervalFormat_createDateIntervalFormat(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName) {
-  ScopedIcuLocale icuLocale(env, javaLocaleName);
-  if (!icuLocale.valid()) {
-    return 0;
-  }
-
-  ScopedJavaUnicodeString skeletonHolder(env, javaSkeleton);
-  if (!skeletonHolder.valid()) {
-    return 0;
-  }
-
-  UErrorCode status = U_ZERO_ERROR;
-  DateIntervalFormat* formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), icuLocale.locale(), status));
-  if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) {
-    return 0;
-  }
-
-  ScopedJavaUnicodeString tzNameHolder(env, javaTzName);
-  if (!tzNameHolder.valid()) {
-    return 0;
-  }
-  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 s;
-  FieldPosition pos = 0;
-  UErrorCode status = U_ZERO_ERROR;
-  formatter->format(&date_interval, s, pos, status);
-  if (maybeThrowIcuException(env, "DateIntervalFormat::format", status)) {
-    return NULL;
-  }
-
-  return env->NewString(s.getBuffer(), s.length());
-}
-
-static JNINativeMethod gMethods[] = {
-  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 d27b11d..0e744b7 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -25,7 +25,6 @@
 #include "ScopedJavaUnicodeString.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "cutils/log.h"
 #include "toStringArray.h"
 #include "unicode/brkiter.h"
@@ -39,6 +38,7 @@
 #include "unicode/locid.h"
 #include "unicode/numfmt.h"
 #include "unicode/strenum.h"
+#include "unicode/timezone.h"
 #include "unicode/ubrk.h"
 #include "unicode/ucal.h"
 #include "unicode/uclean.h"
@@ -62,15 +62,9 @@
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
+#include <memory>
 #include <vector>
 
-// TODO: put this in a header file and use it everywhere!
-// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
-// It goes in the private: declarations in a class.
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-    TypeName(const TypeName&); \
-    void operator=(const TypeName&)
-
 class ScopedResourceBundle {
  public:
   ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) {
@@ -121,7 +115,7 @@
   if (!currencyCode.valid()) {
     return 0;
   }
-  UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+  icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
   UErrorCode status = U_ZERO_ERROR;
   return ucurr_getDefaultFractionDigits(icuCurrencyCode.getTerminatedBuffer(), &status);
 }
@@ -131,7 +125,7 @@
   if (!currencyCode.valid()) {
     return 0;
   }
-  UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+  icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
   return ucurr_getNumericCode(icuCurrencyCode.getTerminatedBuffer());
 }
 
@@ -187,7 +181,7 @@
   if (!currencyCode.valid()) {
     return NULL;
   }
-  UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+  icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
   UErrorCode status = U_ZERO_ERROR;
   UBool isChoiceFormat = false;
   int32_t charCount;
@@ -228,7 +222,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayCountry(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -243,7 +237,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayLanguage(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -258,7 +252,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayScript(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -273,7 +267,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayVariant(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -295,11 +289,11 @@
 }
 
 static jobjectArray ICU_getISOCountriesNative(JNIEnv* env, jclass) {
-    return toStringArray(env, Locale::getISOCountries());
+    return toStringArray(env, icu::Locale::getISOCountries());
 }
 
 static jobjectArray ICU_getISOLanguagesNative(JNIEnv* env, jclass) {
-    return toStringArray(env, Locale::getISOLanguages());
+    return toStringArray(env, icu::Locale::getISOLanguages());
 }
 
 static jobjectArray ICU_getAvailableLocalesNative(JNIEnv* env, jclass) {
@@ -343,7 +337,7 @@
     env->SetObjectField(obj, fid, value);
 }
 
-static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString* valueArray, int32_t size) {
+static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString* valueArray, int32_t size) {
     ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(size, JniConstants::stringClass, NULL));
     for (int32_t i = 0; i < size ; i++) {
         ScopedLocalRef<jstring> s(env, env->NewString(valueArray[i].getBuffer(),valueArray[i].length()));
@@ -369,7 +363,7 @@
   }
 }
 
-static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
+static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
     if (value.length() == 0) {
         return;
     }
@@ -377,43 +371,43 @@
     env->SetCharField(obj, fid, value.charAt(0));
 }
 
-static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
+static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
     const UChar* chars = value.getBuffer();
     setStringField(env, obj, fieldName, env->NewString(chars, value.length()));
 }
 
-static void setNumberPatterns(JNIEnv* env, jobject obj, Locale& locale) {
+static void setNumberPatterns(JNIEnv* env, jobject obj, icu::Locale& locale) {
     UErrorCode status = U_ZERO_ERROR;
 
-    UnicodeString pattern;
-    UniquePtr<DecimalFormat> fmt(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_CURRENCY, status)));
+    icu::UnicodeString pattern;
+    std::unique_ptr<icu::DecimalFormat> fmt(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_CURRENCY, status)));
     pattern = fmt->toPattern(pattern.remove());
     setStringField(env, obj, "currencyPattern", pattern);
 
-    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_DECIMAL, status)));
+    fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_DECIMAL, status)));
     pattern = fmt->toPattern(pattern.remove());
     setStringField(env, obj, "numberPattern", pattern);
 
-    fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_PERCENT, status)));
+    fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_PERCENT, status)));
     pattern = fmt->toPattern(pattern.remove());
     setStringField(env, obj, "percentPattern", pattern);
 }
 
-static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, Locale& locale) {
+static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, icu::Locale& locale) {
     UErrorCode status = U_ZERO_ERROR;
-    DecimalFormatSymbols dfs(locale, status);
+    icu::DecimalFormatSymbols dfs(locale, status);
 
-    setCharField(env, obj, "decimalSeparator", dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol));
-    setCharField(env, obj, "groupingSeparator", dfs.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
-    setCharField(env, obj, "patternSeparator", dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol));
-    setStringField(env, obj, "percent", dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol));
-    setCharField(env, obj, "perMill", dfs.getSymbol(DecimalFormatSymbols::kPerMillSymbol));
-    setCharField(env, obj, "monetarySeparator", dfs.getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol));
-    setStringField(env, obj, "minusSign", dfs.getSymbol(DecimalFormatSymbols:: kMinusSignSymbol));
-    setStringField(env, obj, "exponentSeparator", dfs.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
-    setStringField(env, obj, "infinity", dfs.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
-    setStringField(env, obj, "NaN", dfs.getSymbol(DecimalFormatSymbols::kNaNSymbol));
-    setCharField(env, obj, "zeroDigit", dfs.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
+    setCharField(env, obj, "decimalSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol));
+    setCharField(env, obj, "groupingSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol));
+    setCharField(env, obj, "patternSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kPatternSeparatorSymbol));
+    setStringField(env, obj, "percent", dfs.getSymbol(icu::DecimalFormatSymbols::kPercentSymbol));
+    setCharField(env, obj, "perMill", dfs.getSymbol(icu::DecimalFormatSymbols::kPerMillSymbol));
+    setCharField(env, obj, "monetarySeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kMonetarySeparatorSymbol));
+    setStringField(env, obj, "minusSign", dfs.getSymbol(icu::DecimalFormatSymbols:: kMinusSignSymbol));
+    setStringField(env, obj, "exponentSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kExponentialSymbol));
+    setStringField(env, obj, "infinity", dfs.getSymbol(icu::DecimalFormatSymbols::kInfinitySymbol));
+    setStringField(env, obj, "NaN", dfs.getSymbol(icu::DecimalFormatSymbols::kNaNSymbol));
+    setCharField(env, obj, "zeroDigit", dfs.getSymbol(icu::DecimalFormatSymbols::kZeroDigitSymbol));
 }
 
 
@@ -502,7 +496,7 @@
   return true;
 }
 
-static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const Locale& locale, const char* locale_name) {
+static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const icu::Locale& locale, const char* locale_name) {
   UErrorCode status = U_ZERO_ERROR;
   ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
   ScopedResourceBundle fields(ures_getByKey(root.get(), "fields", NULL, &status));
@@ -512,16 +506,16 @@
     return false;
   }
 
-  UnicodeString yesterday(ures_getUnicodeStringByKey(relative.get(), "-1", &status));
-  UnicodeString today(ures_getUnicodeStringByKey(relative.get(), "0", &status));
-  UnicodeString tomorrow(ures_getUnicodeStringByKey(relative.get(), "1", &status));
+  icu::UnicodeString yesterday(icu::ures_getUnicodeStringByKey(relative.get(), "-1", &status));
+  icu::UnicodeString today(icu::ures_getUnicodeStringByKey(relative.get(), "0", &status));
+  icu::UnicodeString tomorrow(icu::ures_getUnicodeStringByKey(relative.get(), "1", &status));
   if (U_FAILURE(status)) {
     ALOGE("Error getting yesterday/today/tomorrow for %s: %s", locale_name, u_errorName(status));
     return false;
   }
 
   // We title-case the strings so they have consistent capitalization (http://b/14493853).
-  UniquePtr<BreakIterator> brk(BreakIterator::createSentenceInstance(locale, status));
+  std::unique_ptr<icu::BreakIterator> brk(icu::BreakIterator::createSentenceInstance(locale, status));
   if (U_FAILURE(status)) {
     ALOGE("Error getting yesterday/today/tomorrow break iterator for %s: %s", locale_name, u_errorName(status));
     return false;
@@ -591,7 +585,7 @@
     }
 
     status = U_ZERO_ERROR;
-    UniquePtr<Calendar> cal(Calendar::createInstance(icuLocale.locale(), status));
+    std::unique_ptr<icu::Calendar> cal(icu::Calendar::createInstance(icuLocale.locale(), status));
     if (U_FAILURE(status)) {
         return JNI_FALSE;
     }
@@ -601,54 +595,54 @@
 
     // Get DateFormatSymbols.
     status = U_ZERO_ERROR;
-    DateFormatSymbols dateFormatSym(icuLocale.locale(), status);
+    icu::DateFormatSymbols dateFormatSym(icuLocale.locale(), status);
     if (U_FAILURE(status)) {
         return JNI_FALSE;
     }
 
     // Get AM/PM and BC/AD.
     int32_t count = 0;
-    const UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
+    const icu::UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
     setStringArrayField(env, localeData, "amPm", amPmStrs, count);
-    const UnicodeString* erasStrs = dateFormatSym.getEras(count);
+    const icu::UnicodeString* erasStrs = dateFormatSym.getEras(count);
     setStringArrayField(env, localeData, "eras", erasStrs, count);
 
-    const UnicodeString* longMonthNames =
-       dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
+    const icu::UnicodeString* longMonthNames =
+       dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
     setStringArrayField(env, localeData, "longMonthNames", longMonthNames, count);
-    const UnicodeString* shortMonthNames =
-        dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
+    const icu::UnicodeString* shortMonthNames =
+        dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
     setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
-    const UnicodeString* tinyMonthNames =
-        dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
+    const icu::UnicodeString* tinyMonthNames =
+        dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
     setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count);
-    const UnicodeString* longWeekdayNames =
-        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
+    const icu::UnicodeString* longWeekdayNames =
+        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
     setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
-    const UnicodeString* shortWeekdayNames =
-        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
+    const icu::UnicodeString* shortWeekdayNames =
+        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
     setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
-    const UnicodeString* tinyWeekdayNames =
-        dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
+    const icu::UnicodeString* tinyWeekdayNames =
+        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
     setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count);
 
-    const UnicodeString* longStandAloneMonthNames =
-        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
+    const icu::UnicodeString* longStandAloneMonthNames =
+        dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
     setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames, count);
-    const UnicodeString* shortStandAloneMonthNames =
-        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
+    const icu::UnicodeString* shortStandAloneMonthNames =
+        dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
     setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
-    const UnicodeString* tinyStandAloneMonthNames =
-        dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
+    const icu::UnicodeString* tinyStandAloneMonthNames =
+        dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
     setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count);
-    const UnicodeString* longStandAloneWeekdayNames =
-        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
+    const icu::UnicodeString* longStandAloneWeekdayNames =
+        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
     setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
-    const UnicodeString* shortStandAloneWeekdayNames =
-        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
+    const icu::UnicodeString* shortStandAloneWeekdayNames =
+        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
     setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
-    const UnicodeString* tinyStandAloneWeekdayNames =
-        dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
+    const icu::UnicodeString* tinyStandAloneWeekdayNames =
+        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
     setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count);
 
     status = U_ZERO_ERROR;
@@ -687,8 +681,8 @@
   if (!icuLocale.valid()) {
     return NULL;
   }
-  UnicodeString& s(scopedString.unicodeString());
-  UnicodeString original(s);
+  icu::UnicodeString& s(scopedString.unicodeString());
+  icu::UnicodeString original(s);
   s.toLower(icuLocale.locale());
   return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
 }
@@ -702,8 +696,8 @@
   if (!icuLocale.valid()) {
     return NULL;
   }
-  UnicodeString& s(scopedString.unicodeString());
-  UnicodeString original(s);
+  icu::UnicodeString& s(scopedString.unicodeString());
+  icu::UnicodeString original(s);
   s.toUpper(icuLocale.locale());
   return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
 }
@@ -733,9 +727,18 @@
     return versionString(env, unicodeVersion);
 }
 
+static jstring ICU_getTZDataVersion(JNIEnv* env, jclass) {
+  UErrorCode status = U_ZERO_ERROR;
+  const char* version = icu::TimeZone::getTZDataVersion(status);
+  if (maybeThrowIcuException(env, "icu::TimeZone::getTZDataVersion", status)) {
+    return NULL;
+  }
+  return env->NewStringUTF(version);
+}
+
 static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) {
   UErrorCode status = U_ZERO_ERROR;
-  UStringEnumeration e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
+  icu::UStringEnumeration e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
   return fromStringEnumeration(env, status, "ucurr_openISOCurrencies", &e);
 }
 
@@ -746,7 +749,7 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  UniquePtr<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(icuLocale.locale(), status));
+  std::unique_ptr<icu::DateTimePatternGenerator> generator(icu::DateTimePatternGenerator::createInstance(icuLocale.locale(), status));
   if (maybeThrowIcuException(env, "DateTimePatternGenerator::createInstance", status)) {
     return NULL;
   }
@@ -755,7 +758,7 @@
   if (!skeletonHolder.valid()) {
     return NULL;
   }
-  UnicodeString result(generator->getBestPattern(skeletonHolder.unicodeString(), status));
+  icu::UnicodeString result(generator->getBestPattern(skeletonHolder.unicodeString(), status));
   if (maybeThrowIcuException(env, "DateTimePatternGenerator::getBestPattern", status)) {
     return NULL;
   }
@@ -770,12 +773,12 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  Locale::setDefault(icuLocale.locale(), status);
+  icu::Locale::setDefault(icuLocale.locale(), status);
   maybeThrowIcuException(env, "Locale::setDefault", status);
 }
 
 static jstring ICU_getDefaultLocale(JNIEnv* env, jclass) {
-  return env->NewStringUTF(Locale::getDefault().getName());
+  return env->NewStringUTF(icu::Locale::getDefault().getName());
 }
 
 static JNINativeMethod gMethods[] = {
@@ -805,28 +808,25 @@
     NATIVE_METHOD(ICU, getISOLanguagesNative, "()[Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
+    NATIVE_METHOD(ICU, getTZDataVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, initLocaleDataNative, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
     NATIVE_METHOD(ICU, setDefaultLocale, "(Ljava/lang/String;)V"),
     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;"),
 };
-void register_libcore_icu_ICU(JNIEnv* env) {
-    std::string path;
-    path = u_getDataDirectory();
-    path += "/";
-    path += U_ICUDATA_NAME;
-    path += ".dat";
 
-    #define FAIL_WITH_STRERROR(s) \
-        ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
-        abort();
-    #define MAYBE_FAIL_WITH_ICU_ERROR(s) \
-        if (status != U_ZERO_ERROR) {\
-            ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
-            abort(); \
-        }
+#define FAIL_WITH_STRERROR(s) \
+    ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
+    return FALSE;
 
+#define MAYBE_FAIL_WITH_ICU_ERROR(s) \
+    if (status != U_ZERO_ERROR) {\
+        ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
+        return FALSE; \
+    }
+
+static bool mapIcuData(const std::string& path) {
     // Open the file and get its length.
     ScopedFd fd(open(path.c_str(), O_RDONLY));
     if (fd.get() == -1) {
@@ -848,18 +848,66 @@
         FAIL_WITH_STRERROR("madvise(MADV_RANDOM)");
     }
 
-    // Tell ICU to use our memory-mapped data.
     UErrorCode status = U_ZERO_ERROR;
+
+    // Tell ICU to use our memory-mapped data.
     udata_setCommonData(data, &status);
     MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData");
+
+    return TRUE;
+}
+
+void register_libcore_icu_ICU(JNIEnv* env) {
+    // Check the timezone override file exists. If it does, map it first so we use it in preference
+    // to the one that shipped with the device.
+    const char* dataPathPrefix = getenv("ANDROID_DATA");
+    if (dataPathPrefix == NULL) {
+        ALOGE("ANDROID_DATA environment variable not set"); \
+        abort();
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
     // Tell ICU it can *only* use our memory-mapped data.
     udata_setFileAccess(UDATA_NO_FILES, &status);
-    MAYBE_FAIL_WITH_ICU_ERROR("udata_setFileAccess");
+    if (status != U_ZERO_ERROR) {
+        ALOGE("Couldn't initialize ICU (s_setFileAccess): %s", u_errorName(status)); 
+        abort();
+    }
+
+    // Map in optional TZ data files.
+    std::string dataPath;
+    dataPath = dataPathPrefix;
+    dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
+
+    struct stat sb;
+    if (stat(dataPath.c_str(), &sb) == 0) {
+        ALOGD("Timezone override file found: %s", dataPath.c_str());
+        if (!mapIcuData(dataPath)) {
+            ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str());
+        }
+    } else {
+        ALOGD("No timezone override file found: %s", dataPath.c_str());
+    }
+
+    // Use the ICU data files that shipped with the device for everything else.
+    std::string systemPath;
+    systemPath = u_getDataDirectory();
+    systemPath += "/";
+    systemPath += U_ICUDATA_NAME;
+    systemPath += ".dat";
+
+    if (!mapIcuData(systemPath)) {
+        abort();
+    }
 
     // Failures to find the ICU data tend to be somewhat obscure because ICU loads its data on first
     // use, which can be anywhere. Force initialization up front so we can report a nice clear error
     // and bail.
     u_init(&status);
-    MAYBE_FAIL_WITH_ICU_ERROR("u_init");
+    if (status != U_ZERO_ERROR) {\
+        ALOGE("Couldn't initialize ICU (u_init): %s", u_errorName(status));
+        abort();
+    }
+
     jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
deleted file mode 100644
index ef0c2a9..0000000
--- a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-#define LOG_TAG "NativeBreakIterator"
-
-#include "IcuUtilities.h"
-#include "JNIHelp.h"
-#include "JniConstants.h"
-#include "JniException.h"
-#include "ScopedIcuLocale.h"
-#include "ScopedUtfChars.h"
-#include "unicode/brkiter.h"
-#include "unicode/putil.h"
-#include <stdlib.h>
-
-// ICU documentation: http://icu-project.org/apiref/icu4c/classBreakIterator.html
-
-static BreakIterator* toBreakIterator(jlong address) {
-  return reinterpret_cast<BreakIterator*>(static_cast<uintptr_t>(address));
-}
-
-/**
- * We use ICU4C's BreakIterator class, but our input is on the Java heap and potentially moving
- * around between calls. This wrapper class ensures that our RegexMatcher is always pointing at
- * the current location of the char[]. Earlier versions of Android simply copied the data to the
- * native heap, but that's wasteful and hides allocations from the garbage collector.
- */
-class BreakIteratorAccessor {
- public:
-  BreakIteratorAccessor(JNIEnv* env, jlong address, jstring javaInput, bool reset) {
-    init(env, address);
-    mJavaInput = javaInput;
-
-    if (mJavaInput == NULL) {
-      return;
-    }
-
-    mChars = env->GetStringChars(mJavaInput, NULL);
-    if (mChars == NULL) {
-      return;
-    }
-
-    mUText = utext_openUChars(NULL, mChars, env->GetStringLength(mJavaInput), &mStatus);
-    if (mUText == NULL) {
-      return;
-    }
-
-    if (reset) {
-      mBreakIterator->setText(mUText, mStatus);
-    } else {
-      mBreakIterator->refreshInputText(mUText, mStatus);
-    }
-  }
-
-  BreakIteratorAccessor(JNIEnv* env, jlong address) {
-    init(env, address);
-  }
-
-  ~BreakIteratorAccessor() {
-    utext_close(mUText);
-    if (mJavaInput) {
-      mEnv->ReleaseStringChars(mJavaInput, mChars);
-    }
-    maybeThrowIcuException(mEnv, "utext_close", mStatus);
-  }
-
-  BreakIterator* operator->() {
-    return mBreakIterator;
-  }
-
-  UErrorCode& status() {
-    return mStatus;
-  }
-
- private:
-  void init(JNIEnv* env, jlong address) {
-    mEnv = env;
-    mJavaInput = NULL;
-    mBreakIterator = toBreakIterator(address);
-    mChars = NULL;
-    mStatus = U_ZERO_ERROR;
-    mUText = NULL;
-  }
-
-  JNIEnv* mEnv;
-  jstring mJavaInput;
-  BreakIterator* mBreakIterator;
-  const jchar* mChars;
-  UErrorCode mStatus;
-  UText* mUText;
-
-  // Disallow copy and assignment.
-  BreakIteratorAccessor(const BreakIteratorAccessor&);
-  void operator=(const BreakIteratorAccessor&);
-};
-
-#define MAKE_BREAK_ITERATOR_INSTANCE(F) \
-  ScopedIcuLocale icuLocale(env, javaLocaleName); \
-  if (!icuLocale.valid()) { \
-    return 0; \
-  } \
-  UErrorCode status = U_ZERO_ERROR; \
-  BreakIterator* it = F(icuLocale.locale(), status); \
-  if (maybeThrowIcuException(env, "ubrk_open", status)) { \
-    return 0; \
-  } \
-  return reinterpret_cast<uintptr_t>(it)
-
-static jlong NativeBreakIterator_cloneImpl(JNIEnv* env, jclass, jlong address) {
-  BreakIteratorAccessor it(env, address);
-  return reinterpret_cast<uintptr_t>(it->clone());
-}
-
-static void NativeBreakIterator_closeImpl(JNIEnv*, jclass, jlong address) {
-  delete toBreakIterator(address);
-}
-
-static jint NativeBreakIterator_currentImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  return it->current();
-}
-
-static jint NativeBreakIterator_firstImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  return it->first();
-}
-
-static jint NativeBreakIterator_followingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  return it->following(offset);
-}
-
-static jlong NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
-  MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createCharacterInstance);
-}
-
-static jlong NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
-  MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createLineInstance);
-}
-
-static jlong NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
-  MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createSentenceInstance);
-}
-
-static jlong NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
-  MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createWordInstance);
-}
-
-static jboolean NativeBreakIterator_isBoundaryImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  return it->isBoundary(offset);
-}
-
-static jint NativeBreakIterator_lastImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  return it->last();
-}
-
-static jint NativeBreakIterator_nextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint n) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  if (n < 0) {
-    while (n++ < -1) {
-      it->previous();
-    }
-    return it->previous();
-  } else if (n == 0) {
-    return it->current();
-  } else {
-    while (n-- > 1) {
-      it->next();
-    }
-    return it->next();
-  }
-  return -1;
-}
-
-static jint NativeBreakIterator_precedingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  return it->preceding(offset);
-}
-
-static jint NativeBreakIterator_previousImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
-  BreakIteratorAccessor it(env, address, javaInput, false);
-  return it->previous();
-}
-
-static void NativeBreakIterator_setTextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
-  BreakIteratorAccessor it(env, address, javaInput, true);
-}
-
-static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(NativeBreakIterator, cloneImpl, "(J)J"),
-  NATIVE_METHOD(NativeBreakIterator, closeImpl, "(J)V"),
-  NATIVE_METHOD(NativeBreakIterator, currentImpl, "(JLjava/lang/String;)I"),
-  NATIVE_METHOD(NativeBreakIterator, firstImpl, "(JLjava/lang/String;)I"),
-  NATIVE_METHOD(NativeBreakIterator, followingImpl, "(JLjava/lang/String;I)I"),
-  NATIVE_METHOD(NativeBreakIterator, getCharacterInstanceImpl, "(Ljava/lang/String;)J"),
-  NATIVE_METHOD(NativeBreakIterator, getLineInstanceImpl, "(Ljava/lang/String;)J"),
-  NATIVE_METHOD(NativeBreakIterator, getSentenceInstanceImpl, "(Ljava/lang/String;)J"),
-  NATIVE_METHOD(NativeBreakIterator, getWordInstanceImpl, "(Ljava/lang/String;)J"),
-  NATIVE_METHOD(NativeBreakIterator, isBoundaryImpl, "(JLjava/lang/String;I)Z"),
-  NATIVE_METHOD(NativeBreakIterator, lastImpl, "(JLjava/lang/String;)I"),
-  NATIVE_METHOD(NativeBreakIterator, nextImpl, "(JLjava/lang/String;I)I"),
-  NATIVE_METHOD(NativeBreakIterator, precedingImpl, "(JLjava/lang/String;I)I"),
-  NATIVE_METHOD(NativeBreakIterator, previousImpl, "(JLjava/lang/String;)I"),
-  NATIVE_METHOD(NativeBreakIterator, setTextImpl, "(JLjava/lang/String;)V"),
-};
-void register_libcore_icu_NativeBreakIterator(JNIEnv* env) {
-  jniRegisterNativeMethods(env, "libcore/icu/NativeBreakIterator", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/libcore_icu_NativeCollation.cpp b/luni/src/main/native/libcore_icu_NativeCollation.cpp
index 4ce42ec..f27d72e 100644
--- a/luni/src/main/native/libcore_icu_NativeCollation.cpp
+++ b/luni/src/main/native/libcore_icu_NativeCollation.cpp
@@ -15,10 +15,10 @@
 #include "JniException.h"
 #include "ScopedStringChars.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "unicode/ucol.h"
 #include "unicode/ucoleitr.h"
 #include <cutils/log.h>
+#include <memory>
 
 // Manages a UCollationElements instance along with the jchar
 // array it is iterating over. The associated array can be unpinned
@@ -124,7 +124,7 @@
         return -1;
     }
 
-    UniquePtr<CollationElements> ce(new CollationElements);
+    std::unique_ptr<CollationElements> ce(new CollationElements);
     UErrorCode status = ce->start(env, javaSource, toCollator(address));
     maybeThrowIcuException(env, "ucol_openElements", status);
     if (status == U_ZERO_ERROR) {
@@ -156,7 +156,7 @@
     const UCollator* collator  = toCollator(address);
     // The buffer size prevents reallocation for most strings.
     uint8_t byteArray[128];
-    UniquePtr<uint8_t[]> largerByteArray;
+    std::unique_ptr<uint8_t[]> largerByteArray;
     uint8_t* usedByteArray = byteArray;
     size_t byteArraySize = ucol_getSortKey(collator, source.get(), source.size(), usedByteArray, sizeof(byteArray) - 1);
     if (byteArraySize > sizeof(byteArray) - 1) {
diff --git a/luni/src/main/native/libcore_icu_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp
index 8dd439a..355cc78 100644
--- a/luni/src/main/native/libcore_icu_NativeConverter.cpp
+++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp
@@ -23,7 +23,6 @@
 #include "ScopedPrimitiveArray.h"
 #include "ScopedStringChars.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "cutils/log.h"
 #include "toStringArray.h"
 #include "unicode/ucnv.h"
@@ -32,6 +31,7 @@
 #include "unicode/ustring.h"
 #include "unicode/utypes.h"
 
+#include <memory>
 #include <vector>
 
 #include <stdlib.h>
@@ -64,7 +64,7 @@
 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));
+  icu::UStringEnumeration e(ucnv_openStandardNames(canonicalName, standard, &status));
   if (maybeThrowIcuException(env, "ucnv_openStandardNames", status)) {
     return false;
   }
@@ -75,7 +75,7 @@
   }
 
   for (int32_t i = 0; i < count; ++i) {
-    const UnicodeString* string = e.snext(status);
+    const icu::UnicodeString* string = e.snext(status);
     if (maybeThrowIcuException(env, "StringEnumeration::snext", status)) {
       return false;
     }
@@ -104,7 +104,7 @@
   } 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));
+    icu::LocalUConverterPointer cnv(ucnv_open(name + 2, &error));
     if (U_SUCCESS(error)) {
       return name + 2;
     }
@@ -150,7 +150,7 @@
   if (name == NULL) {
     name = icuCanonicalName;
   }
-  UniquePtr<char[]> result(new char[2 + strlen(name) + 1]);
+  std::unique_ptr<char[]> result(new char[2 + strlen(name) + 1]);
   strcpy(&result[0], "x-");
   strcat(&result[0], name);
   return env->NewStringUTF(&result[0]);
@@ -537,12 +537,12 @@
     }
 
     UErrorCode errorCode = U_ZERO_ERROR;
-    LocalUConverterPointer converter1(ucnv_open(name1Chars.c_str(), &errorCode));
-    UnicodeSet set1;
+    icu::LocalUConverterPointer converter1(ucnv_open(name1Chars.c_str(), &errorCode));
+    icu::UnicodeSet set1;
     ucnv_getUnicodeSet(&*converter1, set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
 
-    LocalUConverterPointer converter2(ucnv_open(name2Chars.c_str(), &errorCode));
-    UnicodeSet set2;
+    icu::LocalUConverterPointer converter2(ucnv_open(name2Chars.c_str(), &errorCode));
+    icu::UnicodeSet set2;
     ucnv_getUnicodeSet(&*converter2, set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
 
     return U_SUCCESS(errorCode) && set1.containsAll(set2);
@@ -570,7 +570,7 @@
     {
         // ICU doesn't offer any "isSupported", so we just open and immediately close.
         UErrorCode error = U_ZERO_ERROR;
-        LocalUConverterPointer cnv(ucnv_open(icuCanonicalName, &error));
+        icu::LocalUConverterPointer cnv(ucnv_open(icuCanonicalName, &error));
         if (!U_SUCCESS(error)) {
             return NULL;
         }
diff --git a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
index 8e440e9..8c4a411 100644
--- a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <memory>
 #include <vector>
 
 #include "cutils/log.h"
@@ -36,18 +37,17 @@
 #include "unicode/numfmt.h"
 #include "unicode/unum.h"
 #include "unicode/ustring.h"
-#include "UniquePtr.h"
 #include "valueOf.h"
 
-static DecimalFormat* toDecimalFormat(jlong addr) {
-    return reinterpret_cast<DecimalFormat*>(static_cast<uintptr_t>(addr));
+static icu::DecimalFormat* toDecimalFormat(jlong addr) {
+    return reinterpret_cast<icu::DecimalFormat*>(static_cast<uintptr_t>(addr));
 }
 
 static UNumberFormat* toUNumberFormat(jlong addr) {
     return reinterpret_cast<UNumberFormat*>(static_cast<uintptr_t>(addr));
 }
 
-static DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env,
+static icu::DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env,
         jstring currencySymbol0, jchar decimalSeparator, jchar digit, jstring exponentSeparator0,
         jchar groupingSeparator0, jstring infinity0,
         jstring internationalCurrencySymbol0, jstring minusSign0,
@@ -60,36 +60,41 @@
     ScopedJavaUnicodeString nan(env, nan0);
     ScopedJavaUnicodeString minusSign(env, minusSign0);
     ScopedJavaUnicodeString percent(env, percent0);
-    UnicodeString groupingSeparator(groupingSeparator0);
+    icu::UnicodeString groupingSeparator(groupingSeparator0);
 
-    DecimalFormatSymbols* result = new DecimalFormatSymbols;
-    result->setSymbol(DecimalFormatSymbols::kCurrencySymbol, currencySymbol.unicodeString());
-    result->setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UnicodeString(decimalSeparator));
-    result->setSymbol(DecimalFormatSymbols::kDigitSymbol, UnicodeString(digit));
-    result->setSymbol(DecimalFormatSymbols::kExponentialSymbol, exponentSeparator.unicodeString());
-    result->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, groupingSeparator);
-    result->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator);
-    result->setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString());
-    result->setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString());
-    result->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, minusSign.unicodeString());
-    result->setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, UnicodeString(monetaryDecimalSeparator));
-    result->setSymbol(DecimalFormatSymbols::kNaNSymbol, nan.unicodeString());
-    result->setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, UnicodeString(patternSeparator));
-    result->setSymbol(DecimalFormatSymbols::kPercentSymbol, percent.unicodeString());
-    result->setSymbol(DecimalFormatSymbols::kPerMillSymbol, UnicodeString(perMill));
+    UErrorCode status = U_ZERO_ERROR;
+    std::unique_ptr<icu::DecimalFormatSymbols> result(icu::DecimalFormatSymbols::createWithLastResortData(status));
+    if (maybeThrowIcuException(env, "DecimalFormatSymbols::createWithLastResortData", status)) {
+      return NULL;
+    }
+
+    result->setSymbol(icu::DecimalFormatSymbols::kCurrencySymbol, currencySymbol.unicodeString());
+    result->setSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol, icu::UnicodeString(decimalSeparator));
+    result->setSymbol(icu::DecimalFormatSymbols::kDigitSymbol, icu::UnicodeString(digit));
+    result->setSymbol(icu::DecimalFormatSymbols::kExponentialSymbol, exponentSeparator.unicodeString());
+    result->setSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol, groupingSeparator);
+    result->setSymbol(icu::DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator);
+    result->setSymbol(icu::DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString());
+    result->setSymbol(icu::DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString());
+    result->setSymbol(icu::DecimalFormatSymbols::kMinusSignSymbol, minusSign.unicodeString());
+    result->setSymbol(icu::DecimalFormatSymbols::kMonetarySeparatorSymbol, icu::UnicodeString(monetaryDecimalSeparator));
+    result->setSymbol(icu::DecimalFormatSymbols::kNaNSymbol, nan.unicodeString());
+    result->setSymbol(icu::DecimalFormatSymbols::kPatternSeparatorSymbol, icu::UnicodeString(patternSeparator));
+    result->setSymbol(icu::DecimalFormatSymbols::kPercentSymbol, percent.unicodeString());
+    result->setSymbol(icu::DecimalFormatSymbols::kPerMillSymbol, icu::UnicodeString(perMill));
     // java.text.DecimalFormatSymbols just uses a zero digit,
     // but ICU >= 4.6 has a field for each decimal digit.
-    result->setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, UnicodeString(zeroDigit + 0));
-    result->setSymbol(DecimalFormatSymbols::kOneDigitSymbol, UnicodeString(zeroDigit + 1));
-    result->setSymbol(DecimalFormatSymbols::kTwoDigitSymbol, UnicodeString(zeroDigit + 2));
-    result->setSymbol(DecimalFormatSymbols::kThreeDigitSymbol, UnicodeString(zeroDigit + 3));
-    result->setSymbol(DecimalFormatSymbols::kFourDigitSymbol, UnicodeString(zeroDigit + 4));
-    result->setSymbol(DecimalFormatSymbols::kFiveDigitSymbol, UnicodeString(zeroDigit + 5));
-    result->setSymbol(DecimalFormatSymbols::kSixDigitSymbol, UnicodeString(zeroDigit + 6));
-    result->setSymbol(DecimalFormatSymbols::kSevenDigitSymbol, UnicodeString(zeroDigit + 7));
-    result->setSymbol(DecimalFormatSymbols::kEightDigitSymbol, UnicodeString(zeroDigit + 8));
-    result->setSymbol(DecimalFormatSymbols::kNineDigitSymbol, UnicodeString(zeroDigit + 9));
-    return result;
+    result->setSymbol(icu::DecimalFormatSymbols::kZeroDigitSymbol, icu::UnicodeString(zeroDigit + 0));
+    result->setSymbol(icu::DecimalFormatSymbols::kOneDigitSymbol, icu::UnicodeString(zeroDigit + 1));
+    result->setSymbol(icu::DecimalFormatSymbols::kTwoDigitSymbol, icu::UnicodeString(zeroDigit + 2));
+    result->setSymbol(icu::DecimalFormatSymbols::kThreeDigitSymbol, icu::UnicodeString(zeroDigit + 3));
+    result->setSymbol(icu::DecimalFormatSymbols::kFourDigitSymbol, icu::UnicodeString(zeroDigit + 4));
+    result->setSymbol(icu::DecimalFormatSymbols::kFiveDigitSymbol, icu::UnicodeString(zeroDigit + 5));
+    result->setSymbol(icu::DecimalFormatSymbols::kSixDigitSymbol, icu::UnicodeString(zeroDigit + 6));
+    result->setSymbol(icu::DecimalFormatSymbols::kSevenDigitSymbol, icu::UnicodeString(zeroDigit + 7));
+    result->setSymbol(icu::DecimalFormatSymbols::kEightDigitSymbol, icu::UnicodeString(zeroDigit + 8));
+    result->setSymbol(icu::DecimalFormatSymbols::kNineDigitSymbol, icu::UnicodeString(zeroDigit + 9));
+    return result.release();
 }
 
 static void NativeDecimalFormat_setDecimalFormatSymbols(JNIEnv* env, jclass, jlong addr,
@@ -98,7 +103,7 @@
         jstring internationalCurrencySymbol, jstring minusSign,
         jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
         jstring percent, jchar perMill, jchar zeroDigit) {
-    DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
+    icu::DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
             currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator,
             infinity, internationalCurrencySymbol, minusSign,
             monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
@@ -118,12 +123,12 @@
     if (!pattern.valid()) {
       return 0;
     }
-    DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
+    icu::DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
             currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator,
             infinity, internationalCurrencySymbol, minusSign,
             monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
             zeroDigit);
-    DecimalFormat* fmt = new DecimalFormat(pattern.unicodeString(), symbols, parseError, status);
+    icu::DecimalFormat* fmt = new icu::DecimalFormat(pattern.unicodeString(), symbols, parseError, status);
     if (fmt == NULL) {
         delete symbols;
     }
@@ -136,8 +141,8 @@
 }
 
 static void NativeDecimalFormat_setRoundingMode(JNIEnv*, jclass, jlong addr, jint mode, jdouble increment) {
-    DecimalFormat* fmt = toDecimalFormat(addr);
-    fmt->setRoundingMode(static_cast<DecimalFormat::ERoundingMode>(mode));
+    icu::DecimalFormat* fmt = toDecimalFormat(addr);
+    fmt->setRoundingMode(static_cast<icu::DecimalFormat::ERoundingMode>(mode));
     fmt->setRoundingIncrement(increment);
 }
 
@@ -179,7 +184,7 @@
     UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr);
 
     // Find out how long the result will be...
-    UniquePtr<UChar[]> chars;
+    std::unique_ptr<UChar[]> chars;
     uint32_t charCount = 0;
     uint32_t desiredCount = unum_getTextAttribute(fmt, attr, chars.get(), charCount, &status);
     if (status == U_BUFFER_OVERFLOW_ERROR) {
@@ -197,7 +202,7 @@
     if (!pattern.valid()) {
       return;
     }
-    DecimalFormat* fmt = toDecimalFormat(addr);
+    icu::DecimalFormat* fmt = toDecimalFormat(addr);
     UErrorCode status = U_ZERO_ERROR;
     const char* function;
     if (localized) {
@@ -211,8 +216,8 @@
 }
 
 static jstring NativeDecimalFormat_toPatternImpl(JNIEnv* env, jclass, jlong addr, jboolean localized) {
-    DecimalFormat* fmt = toDecimalFormat(addr);
-    UnicodeString pattern;
+    icu::DecimalFormat* fmt = toDecimalFormat(addr);
+    icu::UnicodeString pattern;
     if (localized) {
         fmt->toLocalizedPattern(pattern);
     } else {
@@ -221,12 +226,12 @@
     return env->NewString(pattern.getBuffer(), pattern.length());
 }
 
-static jcharArray formatResult(JNIEnv* env, const UnicodeString& s, FieldPositionIterator* fpi, jobject javaFieldPositionIterator) {
+static jcharArray formatResult(JNIEnv* env, const icu::UnicodeString& s, icu::FieldPositionIterator* fpi, jobject javaFieldPositionIterator) {
     static jmethodID gFPI_setData = env->GetMethodID(JniConstants::fieldPositionIteratorClass, "setData", "([I)V");
 
     if (fpi != NULL) {
         std::vector<int32_t> data;
-        FieldPosition fp;
+        icu::FieldPosition fp;
         while (fpi->next(fp)) {
             data.push_back(fp.getField());
             data.push_back(fp.getBeginIndex());
@@ -258,10 +263,10 @@
 template <typename T>
 static jcharArray format(JNIEnv* env, jlong addr, jobject javaFieldPositionIterator, T value) {
     UErrorCode status = U_ZERO_ERROR;
-    UnicodeString s;
-    DecimalFormat* fmt = toDecimalFormat(addr);
-    FieldPositionIterator nativeFieldPositionIterator;
-    FieldPositionIterator* fpi = javaFieldPositionIterator ? &nativeFieldPositionIterator : NULL;
+    icu::UnicodeString s;
+    icu::DecimalFormat* fmt = toDecimalFormat(addr);
+    icu::FieldPositionIterator nativeFieldPositionIterator;
+    icu::FieldPositionIterator* fpi = javaFieldPositionIterator ? &nativeFieldPositionIterator : NULL;
     fmt->format(value, s, fpi, status);
     if (maybeThrowIcuException(env, "DecimalFormat::format", status)) {
         return NULL;
@@ -282,7 +287,7 @@
     if (chars.c_str() == NULL) {
         return NULL;
     }
-    StringPiece sp(chars.c_str());
+    icu::StringPiece sp(chars.c_str());
     return format(env, addr, javaFieldPositionIterator, sp);
 }
 
@@ -293,7 +298,7 @@
     // value is a UTF-8 string of invariant characters, but isn't guaranteed to be
     // null-terminated.  NewStringUTF requires a terminated UTF-8 string.  So we copy the
     // data to jchars using UnicodeString, and call NewString instead.
-    UnicodeString tmp(value, len, UnicodeString::kInvariant);
+    icu::UnicodeString tmp(value, len, icu::UnicodeString::kInvariant);
     jobject str = env->NewString(tmp.getBuffer(), tmp.length());
     return env->NewObject(JniConstants::bigDecimalClass, gBigDecimal_init, str);
 }
@@ -318,9 +323,9 @@
         return NULL;
     }
 
-    Formattable res;
-    ParsePosition pp(parsePos);
-    DecimalFormat* fmt = toDecimalFormat(addr);
+    icu::Formattable res;
+    icu::ParsePosition pp(parsePos);
+    icu::DecimalFormat* fmt = toDecimalFormat(addr);
     fmt->parse(src.unicodeString(), res, pp);
 
     if (pp.getErrorIndex() == -1) {
@@ -332,7 +337,7 @@
 
     if (parseBigDecimal) {
         UErrorCode status = U_ZERO_ERROR;
-        StringPiece str = res.getDecimalNumber(status);
+        icu::StringPiece str = res.getDecimalNumber(status);
         if (U_SUCCESS(status)) {
             int len = str.length();
             const char* data = str.data();
@@ -348,15 +353,15 @@
     }
 
     switch (res.getType()) {
-        case Formattable::kDouble: return doubleValueOf(env, res.getDouble());
-        case Formattable::kLong:   return longValueOf(env, res.getLong());
-        case Formattable::kInt64:  return longValueOf(env, res.getInt64());
+        case icu::Formattable::kDouble: return doubleValueOf(env, res.getDouble());
+        case icu::Formattable::kLong:   return longValueOf(env, res.getLong());
+        case icu::Formattable::kInt64:  return longValueOf(env, res.getInt64());
         default:                   return NULL;
     }
 }
 
 static jlong NativeDecimalFormat_cloneImpl(JNIEnv*, jclass, jlong addr) {
-    DecimalFormat* fmt = toDecimalFormat(addr);
+    icu::DecimalFormat* fmt = toDecimalFormat(addr);
     return reinterpret_cast<uintptr_t>(fmt->clone());
 }
 
diff --git a/luni/src/main/native/libcore_icu_NativeIDN.cpp b/luni/src/main/native/libcore_icu_NativeIDN.cpp
index 16a6e1c..43f3ce5 100644
--- a/luni/src/main/native/libcore_icu_NativeIDN.cpp
+++ b/luni/src/main/native/libcore_icu_NativeIDN.cpp
@@ -39,9 +39,18 @@
     }
     UChar dst[256];
     UErrorCode status = U_ZERO_ERROR;
+
+    // We're stuck implementing IDNA-2003 for now since that's what we specify.
+    //
+    // TODO: Change our spec to IDNA-2008 + UTS-46 compatibility processing if
+    // it's safe enough.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
     size_t resultLength = toAscii
         ? uidna_IDNToASCII(src.get(), src.size(), &dst[0], sizeof(dst), flags, NULL, &status)
         : uidna_IDNToUnicode(src.get(), src.size(), &dst[0], sizeof(dst), flags, NULL, &status);
+#pragma GCC diagnostic pop
+
     if (U_FAILURE(status)) {
         jniThrowException(env, "java/lang/IllegalArgumentException", u_errorName(status));
         return NULL;
diff --git a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
index 8ae42d9..2d5e282 100644
--- a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
+++ b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
@@ -30,8 +30,8 @@
   }
   UNormalizationMode mode = static_cast<UNormalizationMode>(intMode);
   UErrorCode status = U_ZERO_ERROR;
-  UnicodeString dst;
-  Normalizer::normalize(src.unicodeString(), mode, 0, dst, status);
+  icu::UnicodeString dst;
+  icu::Normalizer::normalize(src.unicodeString(), mode, 0, dst, status);
   maybeThrowIcuException(env, "Normalizer::normalize", status);
   return dst.isBogus() ? NULL : env->NewString(dst.getBuffer(), dst.length());
 }
@@ -43,7 +43,7 @@
   }
   UNormalizationMode mode = static_cast<UNormalizationMode>(intMode);
   UErrorCode status = U_ZERO_ERROR;
-  UBool result = Normalizer::isNormalized(src.unicodeString(), mode, status);
+  UBool result = icu::Normalizer::isNormalized(src.unicodeString(), mode, status);
   maybeThrowIcuException(env, "Normalizer::isNormalized", status);
   return result;
 }
diff --git a/luni/src/main/native/libcore_icu_NativePluralRules.cpp b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
index 8910a8c..f278485 100644
--- a/luni/src/main/native/libcore_icu_NativePluralRules.cpp
+++ b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
@@ -25,8 +25,8 @@
 
 #include <string>
 
-static PluralRules* toPluralRules(jlong address) {
-    return reinterpret_cast<PluralRules*>(static_cast<uintptr_t>(address));
+static icu::PluralRules* toPluralRules(jlong address) {
+    return reinterpret_cast<icu::PluralRules*>(static_cast<uintptr_t>(address));
 }
 
 static void NativePluralRules_finalizeImpl(JNIEnv*, jclass, jlong address) {
@@ -48,15 +48,15 @@
         localeName[1] = 'i';
     }
 
-    Locale locale = Locale::createFromName(localeName.c_str());
+    icu::Locale locale = icu::Locale::createFromName(localeName.c_str());
     UErrorCode status = U_ZERO_ERROR;
-    PluralRules* result = PluralRules::forLocale(locale, status);
+    icu::PluralRules* result = icu::PluralRules::forLocale(locale, status);
     maybeThrowIcuException(env, "PluralRules::forLocale", status);
     return reinterpret_cast<uintptr_t>(result);
 }
 
 static jint NativePluralRules_quantityForIntImpl(JNIEnv*, jclass, jlong address, jint value) {
-    UnicodeString keyword = toPluralRules(address)->select(value);
+    icu::UnicodeString keyword = toPluralRules(address)->select(value);
     if (keyword == "zero") {
         return 0;
     } else if (keyword == "one") {
diff --git a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
index a7c9098..d30e7a3 100644
--- a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
+++ b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "TimeZoneNames"
 
+#include <memory>
+
 #include "IcuUtilities.h"
 #include "JNIHelp.h"
 #include "JniConstants.h"
@@ -24,32 +26,31 @@
 #include "ScopedJavaUnicodeString.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "unicode/calendar.h"
 #include "unicode/timezone.h"
 #include "unicode/tznames.h"
 
-static bool isUtc(const UnicodeString& id) {
-  static const UnicodeString kEtcUct("Etc/UCT", 7, US_INV);
-  static const UnicodeString kEtcUtc("Etc/UTC", 7, US_INV);
-  static const UnicodeString kEtcUniversal("Etc/Universal", 13, US_INV);
-  static const UnicodeString kEtcZulu("Etc/Zulu", 8, US_INV);
+static bool isUtc(const icu::UnicodeString& id) {
+  static const icu::UnicodeString kEtcUct("Etc/UCT", 7, US_INV);
+  static const icu::UnicodeString kEtcUtc("Etc/UTC", 7, US_INV);
+  static const icu::UnicodeString kEtcUniversal("Etc/Universal", 13, US_INV);
+  static const icu::UnicodeString kEtcZulu("Etc/Zulu", 8, US_INV);
 
-  static const UnicodeString kUct("UCT", 3, US_INV);
-  static const UnicodeString kUtc("UTC", 3, US_INV);
-  static const UnicodeString kUniversal("Universal", 9, US_INV);
-  static const UnicodeString kZulu("Zulu", 4, US_INV);
+  static const icu::UnicodeString kUct("UCT", 3, US_INV);
+  static const icu::UnicodeString kUtc("UTC", 3, US_INV);
+  static const icu::UnicodeString kUniversal("Universal", 9, US_INV);
+  static const icu::UnicodeString kZulu("Zulu", 4, US_INV);
 
   return id == kEtcUct || id == kEtcUtc || id == kEtcUniversal || id == kEtcZulu ||
       id == kUct || id == kUtc || id == kUniversal || id == kZulu;
 }
 
-static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const UnicodeString& s) {
+static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const icu::UnicodeString& s) {
   // Fill in whatever we got. We don't use the display names if they're "GMT[+-]xx:xx"
   // because icu4c doesn't use the up-to-date time zone transition data, so it gets these
   // wrong. TimeZone.getDisplayName creates accurate names on demand.
   // TODO: investigate whether it's worth doing that work once in the Java wrapper instead of on-demand.
-  static const UnicodeString kGmt("GMT", 3, US_INV);
+  static const icu::UnicodeString kGmt("GMT", 3, US_INV);
   if (!s.isBogus() && !s.startsWith(kGmt)) {
     ScopedLocalRef<jstring> javaString(env, env->NewString(s.getBuffer(), s.length()));
     if (javaString.get() == NULL) {
@@ -67,14 +68,14 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  UniquePtr<TimeZoneNames> names(TimeZoneNames::createInstance(icuLocale.locale(), status));
+  std::unique_ptr<icu::TimeZoneNames> names(icu::TimeZoneNames::createInstance(icuLocale.locale(), status));
   if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) {
     return;
   }
 
-  const UDate now(Calendar::getNow());
+  const UDate now(icu::Calendar::getNow());
 
-  static const UnicodeString kUtc("UTC", 3, US_INV);
+  static const icu::UnicodeString kUtc("UTC", 3, US_INV);
 
   size_t id_count = env->GetArrayLength(result);
   for (size_t i = 0; i < id_count; ++i) {
@@ -87,13 +88,13 @@
       return;
     }
 
-    UnicodeString long_std;
+    icu::UnicodeString long_std;
     names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_STANDARD, now, long_std);
-    UnicodeString short_std;
+    icu::UnicodeString short_std;
     names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_STANDARD, now, short_std);
-    UnicodeString long_dst;
+    icu::UnicodeString long_dst;
     names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_DAYLIGHT, now, long_dst);
-    UnicodeString short_dst;
+    icu::UnicodeString short_dst;
     names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_DAYLIGHT, now, short_dst);
 
     if (isUtc(zone_id.unicodeString())) {
@@ -123,7 +124,7 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  UniquePtr<TimeZoneNames> names(TimeZoneNames::createInstance(icuLocale.locale(), status));
+  std::unique_ptr<icu::TimeZoneNames> names(icu::TimeZoneNames::createInstance(icuLocale.locale(), status));
   if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) {
     return NULL;
   }
@@ -133,8 +134,8 @@
     return NULL;
   }
 
-  UnicodeString s;
-  const UDate now(Calendar::getNow());
+  icu::UnicodeString s;
+  const UDate now(icu::Calendar::getNow());
   names->getDisplayName(tz.unicodeString(), UTZNM_EXEMPLAR_LOCATION, now, s);
   return env->NewString(s.getBuffer(), s.length());
 }
diff --git a/luni/src/main/native/libcore_icu_Transliterator.cpp b/luni/src/main/native/libcore_icu_Transliterator.cpp
index 0c52053..ae21565 100644
--- a/luni/src/main/native/libcore_icu_Transliterator.cpp
+++ b/luni/src/main/native/libcore_icu_Transliterator.cpp
@@ -23,8 +23,8 @@
 #include "ScopedJavaUnicodeString.h"
 #include "unicode/translit.h"
 
-static Transliterator* fromPeer(jlong peer) {
-  return reinterpret_cast<Transliterator*>(static_cast<uintptr_t>(peer));
+static icu::Transliterator* fromPeer(jlong peer) {
+  return reinterpret_cast<icu::Transliterator*>(static_cast<uintptr_t>(peer));
 }
 
 static jlong Transliterator_create(JNIEnv* env, jclass, jstring javaId) {
@@ -33,7 +33,7 @@
     return 0;
   }
   UErrorCode status = U_ZERO_ERROR;
-  Transliterator* t = Transliterator::createInstance(id.unicodeString(), UTRANS_FORWARD, status);
+  icu::Transliterator* t = icu::Transliterator::createInstance(id.unicodeString(), UTRANS_FORWARD, status);
   if (maybeThrowIcuException(env, "Transliterator::createInstance", status)) {
     return 0;
   }
@@ -46,18 +46,18 @@
 
 static jobjectArray Transliterator_getAvailableIDs(JNIEnv* env, jclass) {
   UErrorCode status = U_ZERO_ERROR;
-  StringEnumeration* e = Transliterator::getAvailableIDs(status);
+  icu::StringEnumeration* e = icu::Transliterator::getAvailableIDs(status);
   return fromStringEnumeration(env, status, "Transliterator::getAvailableIDs", e);
 }
 
 static jstring Transliterator_transliterate(JNIEnv* env, jclass, jlong peer, jstring javaString) {
-  Transliterator* t = fromPeer(peer);
+  icu::Transliterator* t = fromPeer(peer);
   ScopedJavaUnicodeString string(env, javaString);
   if (!string.valid()) {
     return NULL;
   }
 
-  UnicodeString& s(string.unicodeString());
+  icu::UnicodeString& s(string.unicodeString());
   t->transliterate(s);
   return env->NewString(s.getBuffer(), s.length());
 }
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index 9edbfb8..5122a6c 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -21,32 +21,12 @@
 #include "Portability.h"
 #include "ScopedBytes.h"
 #include "ScopedPrimitiveArray.h"
-#include "UniquePtr.h"
 
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
 
-#if defined(__arm__)
-// 32-bit ARM has load/store alignment restrictions for longs.
-#define LONG_ALIGNMENT_MASK 0x3
-#define INT_ALIGNMENT_MASK 0x0
-#define SHORT_ALIGNMENT_MASK 0x0
-#elif defined(__mips__)
-// MIPS has load/store alignment restrictions for longs, ints and shorts.
-#define LONG_ALIGNMENT_MASK 0x7
-#define INT_ALIGNMENT_MASK 0x3
-#define SHORT_ALIGNMENT_MASK 0x1
-#elif defined(__aarch64__) || defined(__i386__) || defined(__x86_64__)
-// These architectures can load anything at any alignment.
-#define LONG_ALIGNMENT_MASK 0x0
-#define INT_ALIGNMENT_MASK 0x0
-#define SHORT_ALIGNMENT_MASK 0x0
-#else
-#error unknown load/store alignment restrictions for this architecture
-#endif
-
 // Use packed structures for access to unaligned data on targets with alignment restrictions.
 // The compiler will generate appropriate code to access these structures without
 // generating alignment exceptions.
@@ -82,63 +62,31 @@
     // Do 32-bit swaps as long as possible...
     jint* dst = reinterpret_cast<jint*>(dstShorts);
     const jint* src = reinterpret_cast<const jint*>(srcShorts);
-
-    if ((reinterpret_cast<uintptr_t>(dst) & INT_ALIGNMENT_MASK) == 0 &&
-        (reinterpret_cast<uintptr_t>(src) & INT_ALIGNMENT_MASK) == 0) {
-        for (size_t i = 0; i < count / 2; ++i) {
-            jint v = *src++;
-            *dst++ = bswap_2x16(v);
-        }
-        // ...with one last 16-bit swap if necessary.
-        if ((count % 2) != 0) {
-            jshort v = *reinterpret_cast<const jshort*>(src);
-            *reinterpret_cast<jshort*>(dst) = bswap_16(v);
-        }
-    } else {
-        for (size_t i = 0; i < count / 2; ++i) {
-            jint v = get_unaligned<jint>(src++);
-            put_unaligned<jint>(dst++, bswap_2x16(v));
-        }
-        if ((count % 2) != 0) {
-          jshort v = get_unaligned<jshort>(reinterpret_cast<const jshort*>(src));
-          put_unaligned<jshort>(reinterpret_cast<jshort*>(dst), bswap_16(v));
-        }
+    for (size_t i = 0; i < count / 2; ++i) {
+        jint v = get_unaligned<jint>(src++);
+        put_unaligned<jint>(dst++, bswap_2x16(v));
+    }
+    if ((count % 2) != 0) {
+      jshort v = get_unaligned<jshort>(reinterpret_cast<const jshort*>(src));
+      put_unaligned<jshort>(reinterpret_cast<jshort*>(dst), bswap_16(v));
     }
 }
 
 static inline void swapInts(jint* dstInts, const jint* srcInts, size_t count) {
-    if ((reinterpret_cast<uintptr_t>(dstInts) & INT_ALIGNMENT_MASK) == 0 &&
-        (reinterpret_cast<uintptr_t>(srcInts) & INT_ALIGNMENT_MASK) == 0) {
-        for (size_t i = 0; i < count; ++i) {
-            jint v = *srcInts++;
-            *dstInts++ = bswap_32(v);
-        }
-    } else {
-        for (size_t i = 0; i < count; ++i) {
-            jint v = get_unaligned<int>(srcInts++);
-            put_unaligned<jint>(dstInts++, bswap_32(v));
-        }
+    for (size_t i = 0; i < count; ++i) {
+        jint v = get_unaligned<int>(srcInts++);
+        put_unaligned<jint>(dstInts++, bswap_32(v));
     }
 }
 
 static inline void swapLongs(jlong* dstLongs, const jlong* srcLongs, size_t count) {
     jint* dst = reinterpret_cast<jint*>(dstLongs);
     const jint* src = reinterpret_cast<const jint*>(srcLongs);
-    if ((reinterpret_cast<uintptr_t>(dstLongs) & INT_ALIGNMENT_MASK) == 0 &&
-        (reinterpret_cast<uintptr_t>(srcLongs) & INT_ALIGNMENT_MASK) == 0) {
-        for (size_t i = 0; i < count; ++i) {
-          jint v1 = *src++;
-          jint v2 = *src++;
-          *dst++ = bswap_32(v2);
-          *dst++ = bswap_32(v1);
-        }
-    } else {
-        for (size_t i = 0; i < count; ++i) {
-            jint v1 = get_unaligned<jint>(src++);
-            jint v2 = get_unaligned<jint>(src++);
-            put_unaligned<jint>(dst++, bswap_32(v2));
-            put_unaligned<jint>(dst++, bswap_32(v1));
-        }
+    for (size_t i = 0; i < count; ++i) {
+        jint v1 = get_unaligned<jint>(src++);
+        jint v2 = get_unaligned<jint>(src++);
+        put_unaligned<jint>(dst++, bswap_32(v2));
+        put_unaligned<jint>(dst++, bswap_32(v1));
     }
 }
 
@@ -260,39 +208,27 @@
 }
 
 static jshort Memory_peekShortNative(JNIEnv*, jclass, jlong srcAddress) {
-    return *cast<const jshort*>(srcAddress);
+    return get_unaligned<jshort>(cast<const jshort*>(srcAddress));
 }
 
 static void Memory_pokeShortNative(JNIEnv*, jclass, jlong dstAddress, jshort value) {
-    *cast<jshort*>(dstAddress) = value;
+    put_unaligned<jshort>(cast<jshort*>(dstAddress), value);
 }
 
 static jint Memory_peekIntNative(JNIEnv*, jclass, jlong srcAddress) {
-    return *cast<const jint*>(srcAddress);
+    return get_unaligned<jint>(cast<const jint*>(srcAddress));
 }
 
 static void Memory_pokeIntNative(JNIEnv*, jclass, jlong dstAddress, jint value) {
-    *cast<jint*>(dstAddress) = value;
+    put_unaligned<jint>(cast<jint*>(dstAddress), value);
 }
 
 static jlong Memory_peekLongNative(JNIEnv*, jclass, jlong srcAddress) {
-    jlong result;
-    const jlong* src = cast<const jlong*>(srcAddress);
-    if ((srcAddress & LONG_ALIGNMENT_MASK) == 0) {
-        result = *src;
-    } else {
-        result = get_unaligned<jlong>(src);
-    }
-    return result;
+    return get_unaligned<jlong>(cast<const jlong*>(srcAddress));
 }
 
 static void Memory_pokeLongNative(JNIEnv*, jclass, jlong dstAddress, jlong value) {
-    jlong* dst = cast<jlong*>(dstAddress);
-    if ((dstAddress & LONG_ALIGNMENT_MASK) == 0) {
-        *dst = value;
-    } else {
-        put_unaligned<jlong>(dst, value);
-    }
+    put_unaligned<jlong>(cast<jlong*>(dstAddress), value);
 }
 
 static void unsafeBulkCopy(jbyte* dst, const jbyte* src, jint byteCount,
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 7e9b22e..f6af483 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -25,20 +25,20 @@
 #include "NetworkUtilities.h"
 #include "Portability.h"
 #include "readlink.h"
-#include "../../bionic/libc/dns/include/resolv_netid.h"  // For android_getaddrinfofornet.
 #include "ScopedBytes.h"
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "ScopedUtfChars.h"
 #include "toStringArray.h"
-#include "UniquePtr.h"
 
 #include <arpa/inet.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/rtnetlink.h>
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <netpacket/packet.h>
 #include <poll.h>
 #include <pwd.h>
 #include <signal.h>
@@ -61,7 +61,7 @@
 #include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
-
+#include <memory>
 
 #ifndef __unused
 #define __unused __attribute__((__unused__))
@@ -79,6 +79,52 @@
     }
 };
 
+static bool isIPv4MappedAddress(const sockaddr *sa) {
+    const sockaddr_in6 *sin6 = reinterpret_cast<const sockaddr_in6*>(sa);
+    return sa != NULL && sa->sa_family == AF_INET6 &&
+           (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
+            IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr));  // We map 0.0.0.0 to ::, so :: is mapped.
+}
+
+/**
+ * Perform a socket operation that specifies an IP address, possibly falling back from specifying
+ * the address as an IPv4-mapped IPv6 address in a struct sockaddr_in6 to specifying it as an IPv4
+ * address in a struct sockaddr_in.
+ *
+ * This is needed because all sockets created by the java.net APIs are IPv6 sockets, and on those
+ * sockets, IPv4 operations use IPv4-mapped addresses stored in a struct sockaddr_in6. But sockets
+ * created using Posix.socket(AF_INET, ...) are IPv4 sockets and only support operations using IPv4
+ * socket addresses structures.
+ */
+#define NET_IPV4_FALLBACK(jni_env, return_type, syscall_name, java_fd, java_addr, port, null_addr_ok, args...) ({ \
+    return_type _rc = -1; \
+    do { \
+        sockaddr_storage _ss; \
+        socklen_t _salen; \
+        if (java_addr == NULL && null_addr_ok) { \
+            /* No IP address specified (e.g., sendto() on a connected socket). */ \
+            _salen = 0; \
+        } else if (!inetAddressToSockaddr(jni_env, java_addr, port, _ss, _salen)) { \
+            /* Invalid socket address, return -1. inetAddressToSockaddr has already thrown. */ \
+            break; \
+        } \
+        sockaddr* _sa = _salen ? reinterpret_cast<sockaddr*>(&_ss) : NULL; \
+        /* inetAddressToSockaddr always returns an IPv6 sockaddr. Assume that java_fd was created \
+         * by Java API calls, which always create IPv6 socket fds, and pass it in as is. */ \
+        _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \
+        if (_rc == -1 && errno == EAFNOSUPPORT && _salen && isIPv4MappedAddress(_sa)) { \
+            /* We passed in an IPv4 address in an IPv6 sockaddr and the kernel told us that we got \
+             * the address family wrong. Pass in the same address in an IPv4 sockaddr. */ \
+            jni_env->ExceptionClear(); \
+            if (!inetAddressToSockaddrVerbatim(jni_env, java_addr, port, _ss, _salen)) { \
+                break; \
+            } \
+            _sa = reinterpret_cast<sockaddr*>(&_ss); \
+            _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \
+        } \
+    } while (0); \
+    _rc; }) \
+
 /**
  * Used to retry networking system calls that can be interrupted with a signal. Unlike
  * TEMP_FAILURE_RETRY, this also handles the case where
@@ -150,6 +196,9 @@
     } while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \
     _rc; })
 
+#define NULL_ADDR_OK         true
+#define NULL_ADDR_FORBIDDEN  false
+
 static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2,
         const char* functionName, int error) {
     jthrowable cause = NULL;
@@ -268,14 +317,43 @@
 };
 
 static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss) {
-    jint port;
-    jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
-    if (inetAddress == NULL) {
-        return NULL;
+    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6 || ss.ss_family == AF_UNIX) {
+        jint port;
+        jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
+        if (inetAddress == NULL) {
+            return NULL;  // Exception already thrown.
+        }
+        static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass,
+                "<init>", "(Ljava/net/InetAddress;I)V");
+        return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port);
+    } else if (ss.ss_family == AF_NETLINK) {
+        const struct sockaddr_nl* nl_addr = reinterpret_cast<const struct sockaddr_nl*>(&ss);
+        static jmethodID ctor = env->GetMethodID(JniConstants::netlinkSocketAddressClass,
+                "<init>", "(II)V");
+        return env->NewObject(JniConstants::netlinkSocketAddressClass, ctor, 
+                static_cast<jint>(nl_addr->nl_pid), 
+                static_cast<jint>(nl_addr->nl_groups));
+    } else if (ss.ss_family == AF_PACKET) {
+        const struct sockaddr_ll* sll = reinterpret_cast<const struct sockaddr_ll*>(&ss);
+        static jmethodID ctor = env->GetMethodID(JniConstants::packetSocketAddressClass,
+                "<init>", "(SISB[B)V");
+        ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(sll->sll_halen));
+        if (byteArray.get() == NULL) {
+            return NULL;
+        }
+        env->SetByteArrayRegion(byteArray.get(), 0, sll->sll_halen,
+                reinterpret_cast<const jbyte*>(sll->sll_addr));
+        jobject packetSocketAddress = env->NewObject(JniConstants::packetSocketAddressClass, ctor,
+                static_cast<jshort>(ntohs(sll->sll_protocol)),
+                static_cast<jint>(sll->sll_ifindex),
+                static_cast<jshort>(sll->sll_hatype),
+                static_cast<jbyte>(sll->sll_pkttype),
+                byteArray.get());
+        return packetSocketAddress;
     }
-    static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass, "<init>",
-            "(Ljava/net/InetAddress;I)V");
-    return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port);
+    jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "unsupported ss_family: %d",
+            ss.ss_family);
+    return NULL;
 }
 
 static jobject makeStructPasswd(JNIEnv* env, const struct passwd& pw) {
@@ -386,6 +464,93 @@
     return true;
 }
 
+static void javaInetSocketAddressToInetAddressAndPort(
+        JNIEnv* env, jobject javaInetSocketAddress, jobject& javaInetAddress, jint& port) {
+    static jfieldID addressFid = env->GetFieldID(
+            JniConstants::inetSocketAddressClass, "addr", "Ljava/net/InetAddress;");
+    static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "port", "I");
+    javaInetAddress = env->GetObjectField(javaInetSocketAddress, addressFid);
+    port = env->GetIntField(javaInetSocketAddress, portFid);
+}
+
+static bool javaInetSocketAddressToSockaddr(
+        JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+    jobject javaInetAddress;
+    jint port;
+    javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port);
+    return inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len);
+}
+
+static bool javaNetlinkSocketAddressToSockaddr(
+        JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+    static jfieldID nlPidFid = env->GetFieldID(
+            JniConstants::netlinkSocketAddressClass, "nlPortId", "I");
+    static jfieldID nlGroupsFid = env->GetFieldID(
+            JniConstants::netlinkSocketAddressClass, "nlGroupsMask", "I");
+
+    sockaddr_nl *nlAddr = reinterpret_cast<sockaddr_nl *>(&ss);
+    nlAddr->nl_family = AF_NETLINK;
+    nlAddr->nl_pid = env->GetIntField(javaSocketAddress, nlPidFid);
+    nlAddr->nl_groups = env->GetIntField(javaSocketAddress, nlGroupsFid);
+    sa_len = sizeof(sockaddr_nl);
+    return true;
+}
+
+static bool javaPacketSocketAddressToSockaddr(
+        JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+    static jfieldID protocolFid = env->GetFieldID(
+            JniConstants::packetSocketAddressClass, "sll_protocol", "S");
+    static jfieldID ifindexFid = env->GetFieldID(
+            JniConstants::packetSocketAddressClass, "sll_ifindex", "I");
+    static jfieldID hatypeFid = env->GetFieldID(
+            JniConstants::packetSocketAddressClass, "sll_hatype", "S");
+    static jfieldID pkttypeFid = env->GetFieldID(
+            JniConstants::packetSocketAddressClass, "sll_pkttype", "B");
+    static jfieldID addrFid = env->GetFieldID(
+            JniConstants::packetSocketAddressClass, "sll_addr", "[B");
+
+    sockaddr_ll *sll = reinterpret_cast<sockaddr_ll *>(&ss);
+    sll->sll_family = AF_PACKET;
+    sll->sll_protocol = htons(env->GetShortField(javaSocketAddress, protocolFid));
+    sll->sll_ifindex = env->GetIntField(javaSocketAddress, ifindexFid);
+    sll->sll_hatype = env->GetShortField(javaSocketAddress, hatypeFid);
+    sll->sll_pkttype = env->GetByteField(javaSocketAddress, pkttypeFid);
+
+    jbyteArray sllAddr = (jbyteArray) env->GetObjectField(javaSocketAddress, addrFid);
+    if (sllAddr == NULL) {
+        sll->sll_halen = 0;
+        memset(&sll->sll_addr, 0, sizeof(sll->sll_addr));
+    } else {
+        jsize len = env->GetArrayLength(sllAddr);
+        if ((size_t) len > sizeof(sll->sll_addr)) {
+            len = sizeof(sll->sll_addr);
+        }
+        sll->sll_halen = len;
+        env->GetByteArrayRegion(sllAddr, 0, len, (jbyte*) sll->sll_addr);
+    }
+    sa_len = sizeof(sockaddr_ll);
+    return true;
+}
+
+static bool javaSocketAddressToSockaddr(
+        JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+    if (javaSocketAddress == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return false;
+    }
+
+    if (env->IsInstanceOf(javaSocketAddress, JniConstants::netlinkSocketAddressClass)) {
+        return javaNetlinkSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+    } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) {
+        return javaInetSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+    } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::packetSocketAddressClass)) {
+        return javaPacketSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+    }
+    jniThrowException(env, "java/lang/UnsupportedOperationException",
+            "unsupported SocketAddress subclass");
+    return false;
+}
+
 static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) {
     ScopedUtfChars path(env, javaPath);
     if (path.c_str() == NULL) {
@@ -446,7 +611,7 @@
     }
 
     JNIEnv* mEnv;
-    UniquePtr<char[]> mBuffer;
+    std::unique_ptr<char[]> mBuffer;
     size_t mBufferSize;
     struct passwd mPwd;
     struct passwd* mResult;
@@ -479,11 +644,18 @@
 }
 
 static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
+    // We don't need the return value because we'll already have thrown.
+    (void) NET_IPV4_FALLBACK(env, int, bind, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN);
+}
+
+static void Posix_bindSocketAddress(
+        JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
     sockaddr_storage ss;
     socklen_t sa_len;
-    if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
-        return;
+    if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
+        return;  // Exception already thrown.
     }
+
     const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
     // We don't need the return value because we'll already have thrown.
     (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len);
@@ -518,11 +690,17 @@
 }
 
 static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
+    (void) NET_IPV4_FALLBACK(env, int, connect, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN);
+}
+
+static void Posix_connectSocketAddress(
+        JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
     sockaddr_storage ss;
     socklen_t sa_len;
-    if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
-        return;
+    if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
+        return;  // Exception already thrown.
     }
+
     const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
     // We don't need the return value because we'll already have thrown.
     (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sa_len);
@@ -553,7 +731,7 @@
 
     ExecStrings argv(env, javaArgv);
     ExecStrings envp(env, javaEnvp);
-    execve(path.c_str(), argv.get(), envp.get());
+    TEMP_FAILURE_RETRY(execve(path.c_str(), argv.get(), envp.get()));
 
     throwErrnoException(env, "execve");
 }
@@ -565,7 +743,7 @@
     }
 
     ExecStrings argv(env, javaArgv);
-    execv(path.c_str(), argv.get());
+    TEMP_FAILURE_RETRY(execv(path.c_str(), argv.get()));
 
     throwErrnoException(env, "execv");
 }
@@ -580,16 +758,6 @@
     throwIfMinusOne(env, "fchown", TEMP_FAILURE_RETRY(fchown(fd, uid, gid)));
 }
 
-static jint Posix_fcntlVoid(JNIEnv* env, jobject, jobject javaFd, jint cmd) {
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd)));
-}
-
-static jint Posix_fcntlLong(JNIEnv* env, jobject, jobject javaFd, jint cmd, jlong arg) {
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg)));
-}
-
 static jint Posix_fcntlFlock(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaFlock) {
     static jfieldID typeFid = env->GetFieldID(JniConstants::structFlockClass, "l_type", "S");
     static jfieldID whenceFid = env->GetFieldID(JniConstants::structFlockClass, "l_whence", "S");
@@ -616,6 +784,16 @@
     return rc;
 }
 
+static jint Posix_fcntlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jint arg) {
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg)));
+}
+
+static jint Posix_fcntlVoid(JNIEnv* env, jobject, jobject javaFd, jint cmd) {
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd)));
+}
+
 static void Posix_fdatasync(JNIEnv* env, jobject, jobject javaFd) {
     int fd = jniGetFDFromFileDescriptor(env, javaFd);
     throwIfMinusOne(env, "fdatasync", TEMP_FAILURE_RETRY(fdatasync(fd)));
@@ -679,7 +857,7 @@
     addrinfo* addressList = NULL;
     errno = 0;
     int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList);
-    UniquePtr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
+    std::unique_ptr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
     if (rc != 0) {
         throwGaiException(env, "android_getaddrinfo", rc);
         return NULL;
@@ -765,12 +943,16 @@
   return doGetSockName(env, javaFd, false);
 }
 
+static jint Posix_getpgid(JNIEnv* env, jobject, jint pid) {
+    return throwIfMinusOne(env, "getpgid", TEMP_FAILURE_RETRY(getpgid(pid)));
+}
+
 static jint Posix_getpid(JNIEnv*, jobject) {
-    return getpid();
+    return TEMP_FAILURE_RETRY(getpid());
 }
 
 static jint Posix_getppid(JNIEnv*, jobject) {
-    return getppid();
+    return TEMP_FAILURE_RETRY(getppid());
 }
 
 static jobject Posix_getpwnam(JNIEnv* env, jobject, jstring javaName) {
@@ -868,8 +1050,9 @@
     return 0;
   }
   return static_cast<jint>(owner);
+#elif defined(__BIONIC__)
+  return TEMP_FAILURE_RETRY(gettid());
 #else
-  // Neither bionic nor glibc exposes gettid(2).
   return syscall(__NR_gettid);
 #endif
 }
@@ -1036,9 +1219,13 @@
     return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
 }
 
-static jobjectArray Posix_pipe(JNIEnv* env, jobject) {
+static jobjectArray Posix_pipe2(JNIEnv* env, jobject, jint flags __unused) {
+#ifdef __APPLE__
+    jniThrowException(env, "java/lang/UnsupportedOperationException", "no pipe2 on Mac OS");
+    return NULL;
+#else
     int fds[2];
-    throwIfMinusOne(env, "pipe", TEMP_FAILURE_RETRY(pipe(&fds[0])));
+    throwIfMinusOne(env, "pipe2", TEMP_FAILURE_RETRY(pipe2(&fds[0], flags)));
     jobjectArray result = env->NewObjectArray(2, JniConstants::fileDescriptorClass, NULL);
     if (result == NULL) {
         return NULL;
@@ -1054,6 +1241,7 @@
         }
     }
     return result;
+#endif
 }
 
 static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {
@@ -1063,7 +1251,7 @@
 
     // Turn the Java android.system.StructPollfd[] into a C++ struct pollfd[].
     size_t arrayLength = env->GetArrayLength(javaStructs);
-    UniquePtr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
+    std::unique_ptr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
     memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength);
     size_t count = 0; // Some trailing array elements may be irrelevant. (See below.)
     for (size_t i = 0; i < arrayLength; ++i) {
@@ -1084,7 +1272,40 @@
     for (size_t i = 0; i < count; ++i) {
         monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd));
     }
-    int rc = poll(fds.get(), count, timeoutMs);
+
+    int rc;
+    while (true) {
+        timespec before;
+        clock_gettime(CLOCK_MONOTONIC, &before);
+
+        rc = poll(fds.get(), count, timeoutMs);
+        if (rc >= 0 || errno != EINTR) {
+            break;
+        }
+
+        // We got EINTR. Work out how much of the original timeout is still left.
+        if (timeoutMs > 0) {
+            timespec now;
+            clock_gettime(CLOCK_MONOTONIC, &now);
+
+            timespec diff;
+            diff.tv_sec = now.tv_sec - before.tv_sec;
+            diff.tv_nsec = now.tv_nsec - before.tv_nsec;
+            if (diff.tv_nsec < 0) {
+                --diff.tv_sec;
+                diff.tv_nsec += 1000000000;
+            }
+
+            jint diffMs = diff.tv_sec * 1000 + diff.tv_nsec / 1000000;
+            if (diffMs >= timeoutMs) {
+                rc = 0; // We have less than 1ms left anyway, so just time out.
+                break;
+            }
+
+            timeoutMs -= diffMs;
+        }
+    }
+
     for (size_t i = 0; i < monitors.size(); ++i) {
         delete monitors[i];
     }
@@ -1111,7 +1332,8 @@
                       "fallocate doesn't exist on a Mac");
 #else
     int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    errno = TEMP_FAILURE_RETRY(posix_fallocate64(fd, offset, length));
+    while ((errno = posix_fallocate64(fd, offset, length)) == EINTR) {
+    }
     if (errno != 0) {
         throwErrnoException(env, "posix_fallocate");
     }
@@ -1124,9 +1346,11 @@
     jniThrowException(env, "java/lang/UnsupportedOperationException", "prctl doesn't exist on a Mac");
     return 0;
 #else
-    int result = prctl(static_cast<int>(option),
-                       static_cast<unsigned long>(arg2), static_cast<unsigned long>(arg3),
-                       static_cast<unsigned long>(arg4), static_cast<unsigned long>(arg5));
+    int result = TEMP_FAILURE_RETRY(prctl(static_cast<int>(option),
+                                          static_cast<unsigned long>(arg2),
+                                          static_cast<unsigned long>(arg3),
+                                          static_cast<unsigned long>(arg4),
+                                          static_cast<unsigned long>(arg5)));
     return throwIfMinusOne(env, "prctl", result);
 #endif
 }
@@ -1235,13 +1459,35 @@
     if (bytes.get() == NULL) {
         return -1;
     }
-    sockaddr_storage ss;
-    socklen_t sa_len = 0;
-    if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len)) {
+
+    return NET_IPV4_FALLBACK(env, ssize_t, sendto, javaFd, javaInetAddress, port,
+                             NULL_ADDR_OK, bytes.get() + byteOffset, byteCount, flags);
+}
+
+static jint Posix_sendtoBytesSocketAddress(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaSocketAddress) {
+    if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) {
+        // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK.
+        jobject javaInetAddress;
+        jint port;
+        javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port);
+        return Posix_sendtoBytes(env, NULL, javaFd, javaBytes, byteOffset, byteCount, flags,
+                                 javaInetAddress, port);
+    }
+
+    ScopedBytesRO bytes(env, javaBytes);
+    if (bytes.get() == NULL) {
         return -1;
     }
-    const sockaddr* to = (javaInetAddress != NULL) ? reinterpret_cast<const sockaddr*>(&ss) : NULL;
-    return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, sa_len);
+
+    sockaddr_storage ss;
+    socklen_t sa_len;
+    if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
+        return -1;
+    }
+
+    const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
+    // We don't need the return value because we'll already have thrown.
+    return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, sa, sa_len);
 }
 
 static void Posix_setegid(JNIEnv* env, jobject, jint egid) {
@@ -1268,6 +1514,18 @@
     throwIfMinusOne(env, "setgid", TEMP_FAILURE_RETRY(setgid(gid)));
 }
 
+static void Posix_setpgid(JNIEnv* env, jobject, jint pid, int pgid) {
+    throwIfMinusOne(env, "setpgid", TEMP_FAILURE_RETRY(setpgid(pid, pgid)));
+}
+
+static void Posix_setregid(JNIEnv* env, jobject, jint rgid, int egid) {
+    throwIfMinusOne(env, "setregid", TEMP_FAILURE_RETRY(setregid(rgid, egid)));
+}
+
+static void Posix_setreuid(JNIEnv* env, jobject, jint ruid, int euid) {
+    throwIfMinusOne(env, "setreuid", TEMP_FAILURE_RETRY(setreuid(ruid, euid)));
+}
+
 static jint Posix_setsid(JNIEnv* env, jobject) {
     return throwIfMinusOne(env, "setsid", TEMP_FAILURE_RETRY(setsid()));
 }
@@ -1412,6 +1670,9 @@
 }
 
 static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) {
+    if (domain == AF_PACKET) {
+        protocol = htons(protocol);  // Packet sockets specify the protocol in host byte order.
+    }
     int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol)));
     return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
 }
@@ -1531,15 +1792,20 @@
     return IO_FAILURE_RETRY(env, ssize_t, writev, javaFd, ioVec.get(), ioVec.size());
 }
 
+#define NATIVE_METHOD_OVERLOAD(className, functionName, signature, variant) \
+    { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName ## variant) }
+
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
     NATIVE_METHOD(Posix, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"),
     NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
+    NATIVE_METHOD_OVERLOAD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress),
     NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"),
     NATIVE_METHOD(Posix, chown, "(Ljava/lang/String;II)V"),
     NATIVE_METHOD(Posix, close, "(Ljava/io/FileDescriptor;)V"),
     NATIVE_METHOD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
+    NATIVE_METHOD_OVERLOAD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress),
     NATIVE_METHOD(Posix, dup, "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, dup2, "(Ljava/io/FileDescriptor;I)Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, environ, "()[Ljava/lang/String;"),
@@ -1547,9 +1813,9 @@
     NATIVE_METHOD(Posix, execve, "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V"),
     NATIVE_METHOD(Posix, fchmod, "(Ljava/io/FileDescriptor;I)V"),
     NATIVE_METHOD(Posix, fchown, "(Ljava/io/FileDescriptor;II)V"),
-    NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"),
-    NATIVE_METHOD(Posix, fcntlLong, "(Ljava/io/FileDescriptor;IJ)I"),
     NATIVE_METHOD(Posix, fcntlFlock, "(Ljava/io/FileDescriptor;ILandroid/system/StructFlock;)I"),
+    NATIVE_METHOD(Posix, fcntlInt, "(Ljava/io/FileDescriptor;II)I"),
+    NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"),
     NATIVE_METHOD(Posix, fdatasync, "(Ljava/io/FileDescriptor;)V"),
     NATIVE_METHOD(Posix, fstat, "(Ljava/io/FileDescriptor;)Landroid/system/StructStat;"),
     NATIVE_METHOD(Posix, fstatvfs, "(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;"),
@@ -1562,6 +1828,7 @@
     NATIVE_METHOD(Posix, getenv, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(Posix, getnameinfo, "(Ljava/net/InetAddress;I)Ljava/lang/String;"),
     NATIVE_METHOD(Posix, getpeername, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"),
+    NATIVE_METHOD(Posix, getpgid, "(I)I"),
     NATIVE_METHOD(Posix, getpid, "()I"),
     NATIVE_METHOD(Posix, getppid, "()I"),
     NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Landroid/system/StructPasswd;"),
@@ -1595,7 +1862,7 @@
     NATIVE_METHOD(Posix, munlock, "(JJ)V"),
     NATIVE_METHOD(Posix, munmap, "(JJ)V"),
     NATIVE_METHOD(Posix, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;"),
-    NATIVE_METHOD(Posix, pipe, "()[Ljava/io/FileDescriptor;"),
+    NATIVE_METHOD(Posix, pipe2, "(I)[Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, poll, "([Landroid/system/StructPollfd;I)I"),
     NATIVE_METHOD(Posix, posix_fallocate, "(Ljava/io/FileDescriptor;JJ)V"),
     NATIVE_METHOD(Posix, prctl, "(IJJJJ)I"),
@@ -1609,10 +1876,14 @@
     NATIVE_METHOD(Posix, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Posix, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/util/MutableLong;J)J"),
     NATIVE_METHOD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
+    NATIVE_METHOD_OVERLOAD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/SocketAddress;)I", SocketAddress),
     NATIVE_METHOD(Posix, setegid, "(I)V"),
     NATIVE_METHOD(Posix, setenv, "(Ljava/lang/String;Ljava/lang/String;Z)V"),
     NATIVE_METHOD(Posix, seteuid, "(I)V"),
     NATIVE_METHOD(Posix, setgid, "(I)V"),
+    NATIVE_METHOD(Posix, setpgid, "(II)V"),
+    NATIVE_METHOD(Posix, setregid, "(II)V"),
+    NATIVE_METHOD(Posix, setreuid, "(II)V"),
     NATIVE_METHOD(Posix, setsid, "()I"),
     NATIVE_METHOD(Posix, setsockoptByte, "(Ljava/io/FileDescriptor;III)V"),
     NATIVE_METHOD(Posix, setsockoptIfreq, "(Ljava/io/FileDescriptor;IILjava/lang/String;)V"),
diff --git a/luni/src/main/native/java_nio_charset_Charsets.cpp b/luni/src/main/native/libcore_util_CharsetUtils.cpp
similarity index 98%
rename from luni/src/main/native/java_nio_charset_Charsets.cpp
rename to luni/src/main/native/libcore_util_CharsetUtils.cpp
index a49ba22..57c8172 100644
--- a/luni/src/main/native/java_nio_charset_Charsets.cpp
+++ b/luni/src/main/native/libcore_util_CharsetUtils.cpp
@@ -245,6 +245,6 @@
     NATIVE_METHOD(Charsets, toIsoLatin1Bytes, "([CII)[B"),
     NATIVE_METHOD(Charsets, toUtf8Bytes, "([CII)[B"),
 };
-void register_java_nio_charset_Charsets(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "java/nio/charset/Charsets", gMethods, NELEM(gMethods));
+void register_libcore_util_CharsetUtils(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/util/CharsetUtils", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index 2ea8806..48defc1 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -24,11 +24,12 @@
 #include "ScopedPrimitiveArray.h"
 #include "ScopedStringChars.h"
 #include "ScopedUtfChars.h"
-#include "UniquePtr.h"
 #include "jni.h"
 #include "cutils/log.h"
 #include "unicode/unistr.h"
 
+#include <memory>
+
 #include <string.h>
 #include <libexpat/expat.h>
 
@@ -253,7 +254,7 @@
  */
 static InternedString* newInternedString(JNIEnv* env, const char* bytes, int hash) {
     // Allocate a new wrapper.
-    UniquePtr<InternedString> wrapper(new InternedString);
+    std::unique_ptr<InternedString> wrapper(new InternedString);
     if (wrapper.get() == NULL) {
         jniThrowOutOfMemoryError(env, NULL);
         return NULL;
@@ -439,7 +440,7 @@
         return -1;
     }
     UErrorCode status = U_ZERO_ERROR;
-    UnicodeString utf16(UnicodeString::fromUTF8(StringPiece(utf8, byteCount)));
+    icu::UnicodeString utf16(icu::UnicodeString::fromUTF8(icu::StringPiece(utf8, byteCount)));
     return utf16.extract(chars.get(), byteCount, status);
 }
 
@@ -962,7 +963,7 @@
 static jlong ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEncoding,
         jboolean processNamespaces) {
     // Allocate parsing context.
-    UniquePtr<ParsingContext> context(new ParsingContext(object));
+    std::unique_ptr<ParsingContext> context(new ParsingContext(object));
     if (context.get() == NULL) {
         jniThrowOutOfMemoryError(env, NULL);
         return 0;
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 079ecd2..a90c683 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -28,7 +28,6 @@
     java_lang_System.cpp \
     java_math_NativeBN.cpp \
     java_nio_ByteOrder.cpp \
-    java_nio_charset_Charsets.cpp \
     java_text_Bidi.cpp \
     java_util_jar_StrictJarFile.cpp \
     java_util_regex_Matcher.cpp \
@@ -38,9 +37,7 @@
     java_util_zip_Deflater.cpp \
     java_util_zip_Inflater.cpp \
     libcore_icu_AlphabeticIndex.cpp \
-    libcore_icu_DateIntervalFormat.cpp \
     libcore_icu_ICU.cpp \
-    libcore_icu_NativeBreakIterator.cpp \
     libcore_icu_NativeCollation.cpp \
     libcore_icu_NativeConverter.cpp \
     libcore_icu_NativeDecimalFormat.cpp \
@@ -52,18 +49,12 @@
     libcore_io_AsynchronousCloseMonitor.cpp \
     libcore_io_Memory.cpp \
     libcore_io_Posix.cpp \
+    libcore_util_CharsetUtils.cpp \
     org_apache_harmony_xml_ExpatParser.cpp \
     readlink.cpp \
     sun_misc_Unsafe.cpp \
     valueOf.cpp \
 
-LOCAL_C_INCLUDES += \
-    external/icu/icu4c/source/common \
-    external/icu/icu4c/source/i18n \
-    external/openssl/include \
-    external/zlib \
-    system/core/include \
-
 LOCAL_STATIC_LIBRARIES += \
     libfdlibm \
 
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index 1c1296b..7adad72 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -16,10 +16,12 @@
 
 package libcore.icu;
 
-import java.util.Calendar;
-import java.util.Locale;
-import java.util.TimeZone;
-import static libcore.icu.DateIntervalFormat.*;
+import android.icu.util.Calendar;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+
+import static libcore.icu.DateIntervalFormat.formatDateRange;
+import static libcore.icu.DateUtilsBridge.*;
 
 public class DateIntervalFormatTest extends junit.framework.TestCase {
   private static final long MINUTE = 60 * 1000;
@@ -32,7 +34,7 @@
   public void test_formatDateInterval() throws Exception {
     TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
 
-    Calendar c = Calendar.getInstance(tz, Locale.US);
+    Calendar c = Calendar.getInstance(tz, ULocale.US);
     c.set(Calendar.MONTH, Calendar.JANUARY);
     c.set(Calendar.DAY_OF_MONTH, 19);
     c.set(Calendar.HOUR_OF_DAY, 3);
@@ -51,10 +53,10 @@
     long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000;
     long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000;
 
-    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");
+    ULocale de_DE = new ULocale("de", "DE");
+    ULocale en_US = new ULocale("en", "US");
+    ULocale es_ES = new ULocale("es", "ES");
+    ULocale es_US = new ULocale("es", "US");
 
     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));
@@ -81,11 +83,11 @@
     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, 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("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, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+    assertEquals("19/1/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));
@@ -114,60 +116,61 @@
 
     // The same tests but for de_DE.
 
-    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.–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 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. 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, 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));
+    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 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. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19–22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+    assertEquals("19 – 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
-    assertEquals("19 enero–22 abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
-    assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+    assertEquals("19 de ene. – 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
     assertEquals("enero–abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-    assertEquals("19 ene. de 2009–9 feb. de 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 de 2009–9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
-    assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19 de ene. de 2009 – 9 de feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("ene. de 2009 – feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+
+    assertEquals("19 de enero de 2009–9 de febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+    assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
 
     // The same tests but for es_ES.
 
-    assertEquals("19–22 enero 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
+    assertEquals("19–22 de enero de 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. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
-    assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+    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 de enero–jueves, 22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
-    assertEquals("19 enero–22 abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
-    assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("19 de enero–22 de abril de 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 de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-    assertEquals("19 ene. de 2009–9 feb. de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    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 de 2009–9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
-    assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+    assertEquals("19 de enero de 2009–9 de febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+    assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 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 {
-    Locale l = Locale.US;
+    ULocale l = ULocale.US;
     TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
     Calendar c = Calendar.getInstance(tz, l);
     c.clear();
@@ -181,7 +184,7 @@
 
   // http://b/10089890 - we should take the given time zone into account.
   public void test10089890() throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.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;
@@ -203,7 +206,7 @@
     int abbr12 = time12 | FORMAT_ABBREV_ALL;
     int abbr24 = time24 | FORMAT_ABBREV_ALL;
 
-    Locale l = Locale.US;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
 
     // Full length on-the-hour times.
@@ -230,7 +233,7 @@
   // 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;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
 
     long midnight = 0;
@@ -254,7 +257,7 @@
   // 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;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
 
     int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -266,7 +269,7 @@
   // The fix for http://b/10560853 didn't work except for the day around the epoch, which was
   // all the unit test checked!
   public void test_single_day_events_later_than_epoch() throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
 
     int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -284,7 +287,7 @@
   // The fix for http://b/10560853 didn't work except for UTC, which was
   // all the unit test checked!
   public void test_single_day_events_not_in_UTC() throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.US;
     TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
 
     int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -305,7 +308,7 @@
   // 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;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
 
     int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
@@ -328,7 +331,7 @@
   // 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;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
     Calendar c = Calendar.getInstance(utc, l);
     c.set(Calendar.MONTH, Calendar.FEBRUARY);
@@ -363,7 +366,7 @@
 
   // http://b/8467515 - yet another y2k38 bug report.
   public void test8467515() throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
     int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH | FORMAT_ABBREV_WEEKDAY;
     long t;
@@ -383,7 +386,7 @@
   // http://b/12004664
   public void test12004664() throws Exception {
     TimeZone utc = TimeZone.getTimeZone("UTC");
-    Calendar c = Calendar.getInstance(utc, Locale.US);
+    Calendar c = Calendar.getInstance(utc, ULocale.US);
     c.clear();
     c.set(Calendar.YEAR, 1980);
     c.set(Calendar.MONTH, Calendar.FEBRUARY);
@@ -392,26 +395,26 @@
     long thisYear = c.getTimeInMillis();
 
     int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR;
-    assertEquals("Sunday, February 10, 1980", formatDateRange(new Locale("en", "US"), utc, thisYear, thisYear, flags));
+    assertEquals("Sunday, February 10, 1980", formatDateRange(new ULocale("en", "US"), utc, thisYear, thisYear, flags));
 
-    // If we supported non-Gregorian calendars, this is what that we'd expect for these locales.
+    // If we supported non-Gregorian calendars, this is what that we'd expect for these ULocales.
     // This is really the correct behavior, but since java.util.Calendar currently only supports
     // the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have
     // a mix of calendars throughout an app's UI depending on whether Java or native code formatted
     // the date.
-    // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه‍.ش.", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
-    // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
-    // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+    // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه‍.ش.", formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+    // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+    // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
 
     // For now, here are the localized Gregorian strings instead...
-    assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
-    assertEquals("یکشنبه د ۱۹۸۰ د فبروري ۱۰", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
-    assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 1980", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+    assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+    assertEquals("یکشنبه د ۱۹۸۰ د فبروري ۱۰", formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+    assertEquals("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980", formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
   }
 
   // http://b/13234532
   public void test13234532() throws Exception {
-    Locale l = Locale.US;
+    ULocale l = ULocale.US;
     TimeZone utc = TimeZone.getTimeZone("UTC");
 
     int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR;
diff --git a/luni/src/test/java/libcore/icu/ICUTest.java b/luni/src/test/java/libcore/icu/ICUTest.java
index a7cc7a0..99679a7 100644
--- a/luni/src/test/java/libcore/icu/ICUTest.java
+++ b/luni/src/test/java/libcore/icu/ICUTest.java
@@ -20,6 +20,7 @@
 import java.text.Collator;
 import java.util.Arrays;
 import java.util.Locale;
+import libcore.util.ZoneInfoDB;
 
 public class ICUTest extends junit.framework.TestCase {
   public void test_getISOLanguages() throws Exception {
@@ -152,24 +153,24 @@
     assertEquals("sr_ME_#Latn", sr_Latn_ME.toString());
     assertEquals("Latn",        sr_Latn_ME.getScript());
 
-    assertEquals("Српски",              sr_Cyrl_BA.getDisplayLanguage(sr_Cyrl_BA));
+    assertEquals("српски",              sr_Cyrl_BA.getDisplayLanguage(sr_Cyrl_BA));
     assertEquals("Босна и Херцеговина", sr_Cyrl_BA.getDisplayCountry(sr_Cyrl_BA));
-    assertEquals("Ћирилица",            sr_Cyrl_BA.getDisplayScript(sr_Cyrl_BA));
+    assertEquals("ћирилица",            sr_Cyrl_BA.getDisplayScript(sr_Cyrl_BA));
     assertEquals("",                    sr_Cyrl_BA.getDisplayVariant(sr_Cyrl_BA));
 
-    assertEquals("Српски",    sr_Cyrl_ME.getDisplayLanguage(sr_Cyrl_ME));
+    assertEquals("српски",    sr_Cyrl_ME.getDisplayLanguage(sr_Cyrl_ME));
     assertEquals("Црна Гора", sr_Cyrl_ME.getDisplayCountry(sr_Cyrl_ME));
-    assertEquals("Ћирилица",  sr_Cyrl_ME.getDisplayScript(sr_Cyrl_ME));
+    assertEquals("ћирилица",  sr_Cyrl_ME.getDisplayScript(sr_Cyrl_ME));
     assertEquals("",          sr_Cyrl_ME.getDisplayVariant(sr_Cyrl_ME));
 
-    assertEquals("Srpski",              sr_Latn_BA.getDisplayLanguage(sr_Latn_BA));
+    assertEquals("srpski",              sr_Latn_BA.getDisplayLanguage(sr_Latn_BA));
     assertEquals("Bosna i Hercegovina", sr_Latn_BA.getDisplayCountry(sr_Latn_BA));
-    assertEquals("Latinica",            sr_Latn_BA.getDisplayScript(sr_Latn_BA));
+    assertEquals("latinica",            sr_Latn_BA.getDisplayScript(sr_Latn_BA));
     assertEquals("",                    sr_Latn_BA.getDisplayVariant(sr_Latn_BA));
 
-    assertEquals("Srpski",    sr_Latn_ME.getDisplayLanguage(sr_Latn_ME));
+    assertEquals("srpski",    sr_Latn_ME.getDisplayLanguage(sr_Latn_ME));
     assertEquals("Crna Gora", sr_Latn_ME.getDisplayCountry(sr_Latn_ME));
-    assertEquals("Latinica",  sr_Latn_ME.getDisplayScript(sr_Latn_ME));
+    assertEquals("latinica",  sr_Latn_ME.getDisplayScript(sr_Latn_ME));
     assertEquals("",          sr_Latn_ME.getDisplayVariant(sr_Latn_ME));
 
     assertEquals("BIH", sr_Cyrl_BA.getISO3Country());
@@ -253,4 +254,11 @@
       ICU.setDefaultLocale(initialDefaultLocale);
     }
   }
+
+  /** Confirms that ICU agrees with the rest of libcore about the version of the TZ data in use. */
+  public void testTimeZoneDataVersion() {
+    String icu4cTzVersion = ICU.getTZDataVersion();
+    String zoneInfoTzVersion = ZoneInfoDB.getInstance().getVersion();
+    assertEquals(icu4cTzVersion, zoneInfoTzVersion);
+  }
 }
diff --git a/luni/src/test/java/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/icu/LocaleDataTest.java
index 0a83c53..09c3f0f 100644
--- a/luni/src/test/java/libcore/icu/LocaleDataTest.java
+++ b/luni/src/test/java/libcore/icu/LocaleDataTest.java
@@ -24,7 +24,7 @@
     for (Locale l : Locale.getAvailableLocales()) {
       LocaleData d = LocaleData.get(l);
       // System.err.format("%20s %s %s %s\n", l, d.yesterday, d.today, d.tomorrow);
-      // System.err.format("%20s %10s %10s\n", l, d.timeFormat12, d.timeFormat24);
+      // System.err.format("%20s %10s %10s\n", l, d.timeFormat_hm, d.timeFormat_Hm);
     }
   }
 
@@ -73,7 +73,7 @@
 
     assertEquals("leden", l.longStandAloneMonthNames[0]);
     assertEquals("led", l.shortStandAloneMonthNames[0]);
-    assertEquals("l", l.tinyStandAloneMonthNames[0]);
+    assertEquals("1", l.tinyStandAloneMonthNames[0]);
   }
 
   public void test_ko_KR() throws Exception {
@@ -124,11 +124,11 @@
   // http://b/7924970
   public void testTimeFormat12And24() throws Exception {
     LocaleData en_US = LocaleData.get(Locale.US);
-    assertEquals("h:mm a", en_US.timeFormat12);
-    assertEquals("HH:mm", en_US.timeFormat24);
+    assertEquals("h:mm a", en_US.timeFormat_hm);
+    assertEquals("HH:mm", en_US.timeFormat_Hm);
 
     LocaleData ja_JP = LocaleData.get(Locale.JAPAN);
-    assertEquals("aK:mm", ja_JP.timeFormat12);
-    assertEquals("H:mm", ja_JP.timeFormat24);
+    assertEquals("aK:mm", ja_JP.timeFormat_hm);
+    assertEquals("H:mm", ja_JP.timeFormat_Hm);
   }
 }
diff --git a/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
new file mode 100644
index 0000000..101896f
--- /dev/null
+++ b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2015 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.icu;
+
+import android.icu.util.ULocale;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_ALL;
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static libcore.icu.DateUtilsBridge.FORMAT_NO_YEAR;
+import static libcore.icu.DateUtilsBridge.FORMAT_NUMERIC_DATE;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_YEAR;
+import static libcore.icu.RelativeDateTimeFormatter.DAY_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.SECOND_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.WEEK_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.YEAR_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeDateTimeString;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeTimeSpanString;
+
+public class RelativeDateTimeFormatterTest extends junit.framework.TestCase {
+
+  // Tests adopted from CTS tests for DateUtils.getRelativeTimeSpanString.
+  public void test_getRelativeTimeSpanStringCTS() throws Exception {
+    Locale en_US = new Locale("en", "US");
+    TimeZone tz = TimeZone.getTimeZone("GMT");
+    Calendar cal = Calendar.getInstance(tz, en_US);
+    // Feb 5, 2015 at 10:50 GMT
+    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+    final long baseTime = cal.getTimeInMillis();
+
+    assertEquals("0 minutes ago",
+                 getRelativeTimeSpanString(en_US, tz, baseTime - SECOND_IN_MILLIS, baseTime,
+                                           MINUTE_IN_MILLIS, 0));
+    assertEquals("In 0 minutes",
+                 getRelativeTimeSpanString(en_US, tz, baseTime + SECOND_IN_MILLIS, baseTime,
+                                           MINUTE_IN_MILLIS, 0));
+
+    assertEquals("1 minute ago",
+                 getRelativeTimeSpanString(en_US, tz, 0, MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 0));
+    assertEquals("In 1 minute",
+                 getRelativeTimeSpanString(en_US, tz, MINUTE_IN_MILLIS, 0, MINUTE_IN_MILLIS, 0));
+
+    assertEquals("42 minutes ago",
+      getRelativeTimeSpanString(en_US, tz, baseTime - 42 * MINUTE_IN_MILLIS, baseTime,
+                                MINUTE_IN_MILLIS, 0));
+    assertEquals("In 42 minutes",
+      getRelativeTimeSpanString(en_US, tz, baseTime + 42 * MINUTE_IN_MILLIS, baseTime,
+                                MINUTE_IN_MILLIS, 0));
+
+    final long TWO_HOURS_IN_MS = 2 * HOUR_IN_MILLIS;
+    assertEquals("2 hours ago",
+                 getRelativeTimeSpanString(en_US, tz, baseTime - TWO_HOURS_IN_MS, baseTime,
+                                           MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+    assertEquals("In 2 hours",
+                 getRelativeTimeSpanString(en_US, tz, baseTime + TWO_HOURS_IN_MS, baseTime,
+                                           MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+
+    assertEquals("In 42 min.",
+                 getRelativeTimeSpanString(en_US, tz, baseTime + (42 * MINUTE_IN_MILLIS), baseTime,
+                                           MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+
+    assertEquals("Tomorrow",
+                 getRelativeTimeSpanString(en_US, tz, DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+    assertEquals("In 2 days",
+                 getRelativeTimeSpanString(en_US, tz, 2 * DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+    assertEquals("Yesterday",
+                 getRelativeTimeSpanString(en_US, tz, 0, DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+    assertEquals("2 days ago",
+                 getRelativeTimeSpanString(en_US, tz, 0, 2 * DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+
+    final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+    assertEquals("5 days ago",
+                 getRelativeTimeSpanString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+                                           DAY_IN_MILLIS, 0));
+  }
+
+  private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, int flags,
+                                                     String expectedInPast,
+                                                     String expectedInFuture) throws Exception {
+    Locale en_US = new Locale("en", "US");
+    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+    Calendar cal = Calendar.getInstance(tz, en_US);
+    // Feb 5, 2015 at 10:50 PST
+    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+    final long base = cal.getTimeInMillis();
+
+    assertEquals(expectedInPast,
+                 getRelativeTimeSpanString(en_US, tz, base - delta, base, minResolution, flags));
+    assertEquals(expectedInFuture,
+                 getRelativeTimeSpanString(en_US, tz, base + delta, base, minResolution, flags));
+  }
+
+  private void test_getRelativeTimeSpanString_helper(long delta, long minResolution,
+                                                     String expectedInPast,
+                                                     String expectedInFuture) throws Exception {
+    test_getRelativeTimeSpanString_helper(delta, minResolution, 0, expectedInPast, expectedInFuture);
+  }
+
+  public void test_getRelativeTimeSpanString() throws Exception {
+
+    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, "0 seconds ago", "0 seconds ago");
+    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
+    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
+    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, "5 days ago", "In 5 days");
+
+    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "0 seconds ago",
+                                          "0 seconds ago");
+    test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 second ago",
+                                          "In 1 second");
+    test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "2 seconds ago",
+                                          "In 2 seconds");
+    test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "25 seconds ago",
+                                          "In 25 seconds");
+    test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 minute ago",
+                                          "In 1 minute");
+    test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 hour ago",
+                                          "In 1 hour");
+
+    test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
+                                          "0 minutes ago");
+    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
+                                          "In 1 minute");
+    test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "2 minutes ago",
+                                          "In 2 minutes");
+    test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "25 minutes ago",
+                                          "In 25 minutes");
+    test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 hour ago",
+                                          "In 1 hour");
+    test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "12 hours ago",
+                                          "In 12 hours");
+
+    test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+                                          "0 hours ago");
+    test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+                                          "In 1 hour");
+    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "2 hours ago",
+                                          "In 2 hours");
+    test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "5 hours ago",
+                                          "In 5 hours");
+    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "20 hours ago",
+                                          "In 20 hours");
+
+    test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                                          "Tomorrow");
+    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                                          "Tomorrow");
+    test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+                                          "In 2 days");
+    test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, "January 11",
+                                          "March 2");
+
+    test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+                                          "0 weeks ago");
+    test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+                                          "In 1 week");
+    test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "2 weeks ago",
+                                          "In 2 weeks");
+    test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "25 weeks ago",
+                                          "In 25 weeks");
+
+    // duration >= minResolution
+    test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, "30 seconds ago",
+                                          "In 30 seconds");
+    test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+                                          "30 minutes ago", "In 30 minutes");
+    test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, "Yesterday",
+                                          "Tomorrow");
+    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, "5 days ago",
+                                          "In 5 days");
+    test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, "July 10, 2014",
+                                          "September 3");
+    test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS,
+                                          "February 6, 2010", "February 4, 2020");
+
+    test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
+                                          "In 1 minute");
+    test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS,
+                                          "1 minute ago", "In 1 minute");
+    test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+                                          "In 1 hour");
+    test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, "1 hour ago",
+                                          "In 1 hour");
+    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+    test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                                          "Today");
+    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                                          "Tomorrow");
+    test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+                                          "In 2 days");
+    test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+                                          "In 2 days");
+    test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+                                          "In 1 week");
+    test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, "1 week ago",
+                                          "In 1 week");
+
+    // duration < minResolution
+    test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
+                                          "In 0 minutes");
+    test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+                                          "In 0 hours");
+    test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, "0 hours ago",
+                                          "In 0 hours");
+    test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, "Yesterday",
+                                          "Tomorrow");
+    test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+                                          "In 0 weeks");
+    test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, "0 weeks ago",
+                                          "In 0 weeks");
+  }
+
+  public void test_getRelativeTimeSpanStringAbbrev() throws Exception {
+    int flags = FORMAT_ABBREV_RELATIVE;
+
+    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, flags, "0 sec. ago",
+                                          "0 sec. ago");
+    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, flags, "1 min. ago",
+                                          "In 1 min.");
+    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, flags, "5 days ago", "In 5 days");
+
+    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                                          "0 sec. ago", "0 sec. ago");
+    test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                                          "1 sec. ago", "In 1 sec.");
+    test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                                          "2 sec. ago", "In 2 sec.");
+    test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                                          "25 sec. ago", "In 25 sec.");
+    test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                                          "1 min. ago", "In 1 min.");
+    test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                                          "1 hr. ago", "In 1 hr.");
+
+    test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "0 min. ago", "0 min. ago");
+    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "1 min. ago", "In 1 min.");
+    test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "2 min. ago", "In 2 min.");
+    test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "25 min. ago", "In 25 min.");
+    test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "1 hr. ago", "In 1 hr.");
+    test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "12 hr. ago", "In 12 hr.");
+
+    test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                                          "0 hr. ago", "0 hr. ago");
+    test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                                          "1 hr. ago", "In 1 hr.");
+    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                                          "2 hr. ago", "In 2 hr.");
+    test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                                          "5 hr. ago", "In 5 hr.");
+    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                                          "20 hr. ago", "In 20 hr.");
+
+    test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+                                          "Today");
+    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "Yesterday", "Tomorrow");
+    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "Yesterday", "Tomorrow");
+    test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "2 days ago", "In 2 days");
+    test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "January 11", "March 2");
+
+    test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                                          "0 wk. ago", "0 wk. ago");
+    test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                                          "1 wk. ago", "In 1 wk.");
+    test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                                          "2 wk. ago", "In 2 wk.");
+    test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                                          "25 wk. ago", "In 25 wk.");
+
+    // duration >= minResolution
+    test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, flags, "30 sec. ago",
+                                          "In 30 sec.");
+    test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "30 min. ago", "In 30 min.");
+    test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "Yesterday", "Tomorrow");
+    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "5 days ago", "In 5 days");
+    test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "July 10, 2014", "September 3");
+    test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "February 6, 2010", "February 4, 2020");
+
+    test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "1 min. ago", "In 1 min.");
+    test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, flags,
+                                          "1 min. ago", "In 1 min.");
+    test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                                          "1 hr. ago", "In 1 hr.");
+    test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+                                          "1 hr. ago", "In 1 hr.");
+    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+                                          "Today");
+    test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "Yesterday", "Today");
+    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "Yesterday", "Tomorrow");
+    test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "2 days ago", "In 2 days");
+    test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                                          "2 days ago", "In 2 days");
+    test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                                          "1 wk. ago", "In 1 wk.");
+    test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+                                          "1 wk. ago", "In 1 wk.");
+
+    // duration < minResolution
+    test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                                          "0 min. ago", "In 0 min.");
+    test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                                          "0 hr. ago", "In 0 hr.");
+    test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+                                          "0 hr. ago", "In 0 hr.");
+    test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, flags,
+                                          "Yesterday", "Tomorrow");
+    test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                                          "0 wk. ago", "In 0 wk.");
+    test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+                                          "0 wk. ago", "In 0 wk.");
+
+  }
+
+  public void test_getRelativeTimeSpanStringGerman() throws Exception {
+    // Bug: 19744876
+    // We need to specify the timezone and the time explicitly. Otherwise it
+    // may not always give a correct answer of "tomorrow" by using
+    // (now + DAY_IN_MILLIS).
+    Locale de_DE = new Locale("de", "DE");
+    TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
+    Calendar cal = Calendar.getInstance(tz, de_DE);
+    // Feb 5, 2015 at 10:50 CET
+    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+    final long now = cal.getTimeInMillis();
+
+    // 42 minutes ago
+    assertEquals("Vor 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+        now - 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+    // In 42 minutes
+    assertEquals("In 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+        now + 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+    // Yesterday
+    assertEquals("Gestern", getRelativeTimeSpanString(de_DE, tz,
+        now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    // The day before yesterday
+    assertEquals("Vorgestern", getRelativeTimeSpanString(de_DE, tz,
+        now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    // Tomorrow
+    assertEquals("Morgen", getRelativeTimeSpanString(de_DE, tz,
+        now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    // The day after tomorrow
+    assertEquals("Übermorgen", getRelativeTimeSpanString(de_DE, tz,
+        now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+  }
+
+  public void test_getRelativeTimeSpanStringFrench() throws Exception {
+    Locale fr_FR = new Locale("fr", "FR");
+    TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
+    Calendar cal = Calendar.getInstance(tz, fr_FR);
+    // Feb 5, 2015 at 10:50 CET
+    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+    final long now = cal.getTimeInMillis();
+
+    // 42 minutes ago
+    assertEquals("Il y a 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+        now - (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+    // In 42 minutes
+    assertEquals("Dans 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+        now + (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+    // Yesterday
+    assertEquals("Hier", getRelativeTimeSpanString(fr_FR, tz,
+        now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    // The day before yesterday
+    assertEquals("Avant-hier", getRelativeTimeSpanString(fr_FR, tz,
+        now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    // Tomorrow
+    assertEquals("Demain", getRelativeTimeSpanString(fr_FR, tz,
+        now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    // The day after tomorrow
+    assertEquals("Après-demain", getRelativeTimeSpanString(fr_FR, tz,
+        now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+  }
+
+  // Tests adopted from CTS tests for DateUtils.getRelativeDateTimeString.
+  public void test_getRelativeDateTimeStringCTS() throws Exception {
+    Locale en_US = Locale.getDefault();
+    TimeZone tz = TimeZone.getDefault();
+    final long baseTime = System.currentTimeMillis();
+
+    final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+    assertNotNull(getRelativeDateTimeString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+                                            MINUTE_IN_MILLIS, DAY_IN_MILLIS,
+                                            FORMAT_NUMERIC_DATE));
+  }
+
+  public void test_getRelativeDateTimeString() throws Exception {
+    Locale en_US = new Locale("en", "US");
+    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+    Calendar cal = Calendar.getInstance(tz, en_US);
+    // Feb 5, 2015 at 10:50 PST
+    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+    final long base = cal.getTimeInMillis();
+
+    assertEquals("5 seconds ago, 10:49 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+                                           MINUTE_IN_MILLIS, 0));
+    assertEquals("5 min. ago, 10:45 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+                                           HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+    assertEquals("0 hr. ago, 10:45 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base,
+                                           HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+    assertEquals("5 hours ago, 5:50 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base,
+                                           HOUR_IN_MILLIS, DAY_IN_MILLIS, 0));
+    assertEquals("Yesterday, 7:50 PM",
+                 getRelativeDateTimeString(en_US, tz, base - 15 * HOUR_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+    assertEquals("5 days ago, 10:50 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+    assertEquals("Jan 29, 10:50 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 7 * DAY_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+    assertEquals("11/27/2014, 10:50 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+    assertEquals("11/27/2014, 10:50 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                                           YEAR_IN_MILLIS, 0));
+
+    // User-supplied flags should be ignored when formatting the date clause.
+    final int FORMAT_SHOW_WEEKDAY = 0x00002;
+    assertEquals("11/27/2014, 10:50 AM",
+                 getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS,
+                                           FORMAT_ABBREV_ALL | FORMAT_SHOW_WEEKDAY));
+  }
+
+  public void test_getRelativeDateTimeStringDST() throws Exception {
+    Locale en_US = new Locale("en", "US");
+    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+    Calendar cal = Calendar.getInstance(tz, en_US);
+
+    // DST starts on Mar 9, 2014 at 2:00 AM.
+    // So 5 hours before 3:15 AM should be formatted as 'Yesterday, 9:15 PM'.
+    cal.set(2014, Calendar.MARCH, 9, 3, 15, 0);
+    long base = cal.getTimeInMillis();
+    assertEquals("Yesterday, 9:15 PM",
+                 getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+
+    // 1 hour after 2:00 AM should be formatted as 'In 1 hour, 4:00 AM'.
+    cal.set(2014, Calendar.MARCH, 9, 2, 0, 0);
+    base = cal.getTimeInMillis();
+    assertEquals("In 1 hour, 4:00 AM",
+                 getRelativeDateTimeString(en_US, tz, base + 1 * HOUR_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+
+    // DST ends on Nov 2, 2014 at 2:00 AM. Clocks are turned backward 1 hour to
+    // 1:00 AM. 8 hours before 5:20 AM should be 'Yesterday, 10:20 PM'.
+    cal.set(2014, Calendar.NOVEMBER, 2, 5, 20, 0);
+    base = cal.getTimeInMillis();
+    assertEquals("Yesterday, 10:20 PM",
+                 getRelativeDateTimeString(en_US, tz, base - 8 * HOUR_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+
+    cal.set(2014, Calendar.NOVEMBER, 2, 0, 45, 0);
+    base = cal.getTimeInMillis();
+    // 45 minutes after 0:45 AM should be 'In 45 minutes, 1:30 AM'.
+    assertEquals("In 45 minutes, 1:30 AM",
+                 getRelativeDateTimeString(en_US, tz, base + 45 * MINUTE_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+    // 45 minutes later, it should be 'In 45 minutes, 1:15 AM'.
+    assertEquals("In 45 minutes, 1:15 AM",
+                 getRelativeDateTimeString(en_US, tz, base + 90 * MINUTE_IN_MILLIS,
+                                           base + 45 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+    // Another 45 minutes later, it should be 'In 45 minutes, 2:00 AM'.
+    assertEquals("In 45 minutes, 2:00 AM",
+                 getRelativeDateTimeString(en_US, tz, base + 135 * MINUTE_IN_MILLIS,
+                                           base + 90 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+  }
+
+
+  public void test_getRelativeDateTimeStringItalian() throws Exception {
+    Locale it_IT = new Locale("it", "IT");
+    TimeZone tz = TimeZone.getTimeZone("Europe/Rome");
+    Calendar cal = Calendar.getInstance(tz, it_IT);
+    // 05 febbraio 2015 20:15
+    cal.set(2015, Calendar.FEBRUARY, 5, 20, 15, 0);
+    final long base = cal.getTimeInMillis();
+
+    assertEquals("5 secondi fa, 20:14",
+                 getRelativeDateTimeString(it_IT, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+                                           MINUTE_IN_MILLIS, 0));
+    assertEquals("5 min. fa, 20:10",
+                 getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+                                           HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+    assertEquals("0 h. fa, 20:10",
+                 getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base,
+                                           HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+    assertEquals("Ieri, 22:15",
+                 getRelativeDateTimeString(it_IT, tz, base - 22 * HOUR_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+    assertEquals("5 giorni fa, 20:15",
+                 getRelativeDateTimeString(it_IT, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+    assertEquals("27/11/2014, 20:15",
+                 getRelativeDateTimeString(it_IT, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                                           WEEK_IN_MILLIS, 0));
+  }
+
+  // http://b/5252772: detect the actual date difference
+  public void test5252772() throws Exception {
+    Locale en_US = new Locale("en", "US");
+    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+
+    // Now is Sep 2, 2011, 10:23 AM PDT.
+    Calendar nowCalendar = Calendar.getInstance(tz, en_US);
+    nowCalendar.set(2011, Calendar.SEPTEMBER, 2, 10, 23, 0);
+    final long now = nowCalendar.getTimeInMillis();
+
+    // Sep 1, 2011, 10:24 AM
+    Calendar yesterdayCalendar1 = Calendar.getInstance(tz, en_US);
+    yesterdayCalendar1.set(2011, Calendar.SEPTEMBER, 1, 10, 24, 0);
+    long yesterday1 = yesterdayCalendar1.getTimeInMillis();
+    assertEquals("Yesterday, 10:24 AM",
+                 getRelativeDateTimeString(en_US, tz, yesterday1, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+
+    // Sep 1, 2011, 10:22 AM
+    Calendar yesterdayCalendar2 = Calendar.getInstance(tz, en_US);
+    yesterdayCalendar2.set(2011, Calendar.SEPTEMBER, 1, 10, 22, 0);
+    long yesterday2 = yesterdayCalendar2.getTimeInMillis();
+    assertEquals("Yesterday, 10:22 AM",
+                 getRelativeDateTimeString(en_US, tz, yesterday2, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+
+    // Aug 31, 2011, 10:24 AM
+    Calendar twoDaysAgoCalendar1 = Calendar.getInstance(tz, en_US);
+    twoDaysAgoCalendar1.set(2011, Calendar.AUGUST, 31, 10, 24, 0);
+    long twoDaysAgo1 = twoDaysAgoCalendar1.getTimeInMillis();
+    assertEquals("2 days ago, 10:24 AM",
+                 getRelativeDateTimeString(en_US, tz, twoDaysAgo1, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+
+    // Aug 31, 2011, 10:22 AM
+    Calendar twoDaysAgoCalendar2 = Calendar.getInstance(tz, en_US);
+    twoDaysAgoCalendar2.set(2011, Calendar.AUGUST, 31, 10, 22, 0);
+    long twoDaysAgo2 = twoDaysAgoCalendar2.getTimeInMillis();
+    assertEquals("2 days ago, 10:22 AM",
+                 getRelativeDateTimeString(en_US, tz, twoDaysAgo2, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+
+    // Sep 3, 2011, 10:22 AM
+    Calendar tomorrowCalendar1 = Calendar.getInstance(tz, en_US);
+    tomorrowCalendar1.set(2011, Calendar.SEPTEMBER, 3, 10, 22, 0);
+    long tomorrow1 = tomorrowCalendar1.getTimeInMillis();
+    assertEquals("Tomorrow, 10:22 AM",
+                 getRelativeDateTimeString(en_US, tz, tomorrow1, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+
+    // Sep 3, 2011, 10:24 AM
+    Calendar tomorrowCalendar2 = Calendar.getInstance(tz, en_US);
+    tomorrowCalendar2.set(2011, Calendar.SEPTEMBER, 3, 10, 24, 0);
+    long tomorrow2 = tomorrowCalendar2.getTimeInMillis();
+    assertEquals("Tomorrow, 10:24 AM",
+                 getRelativeDateTimeString(en_US, tz, tomorrow2, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+
+    // Sep 4, 2011, 10:22 AM
+    Calendar twoDaysLaterCalendar1 = Calendar.getInstance(tz, en_US);
+    twoDaysLaterCalendar1.set(2011, Calendar.SEPTEMBER, 4, 10, 22, 0);
+    long twoDaysLater1 = twoDaysLaterCalendar1.getTimeInMillis();
+    assertEquals("In 2 days, 10:22 AM",
+                 getRelativeDateTimeString(en_US, tz, twoDaysLater1, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+
+    // Sep 4, 2011, 10:24 AM
+    Calendar twoDaysLaterCalendar2 = Calendar.getInstance(tz, en_US);
+    twoDaysLaterCalendar2.set(2011, Calendar.SEPTEMBER, 4, 10, 24, 0);
+    long twoDaysLater2 = twoDaysLaterCalendar2.getTimeInMillis();
+    assertEquals("In 2 days, 10:24 AM",
+                 getRelativeDateTimeString(en_US, tz, twoDaysLater2, now, MINUTE_IN_MILLIS,
+                                           WEEK_IN_MILLIS, 0));
+  }
+
+  // b/19822016: show / hide the year based on the dates in the arguments.
+  public void test_bug19822016() throws Exception {
+    Locale en_US = new Locale("en", "US");
+    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+    Calendar cal = Calendar.getInstance(tz, en_US);
+    // Feb 5, 2012 at 10:50 PST
+    cal.set(2012, Calendar.FEBRUARY, 5, 10, 50, 0);
+    long base = cal.getTimeInMillis();
+
+    assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+        base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+    assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+        base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+    assertEquals("11/27/2011, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+        base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+    assertEquals("January 6, 2012", getRelativeTimeSpanString(en_US, tz,
+        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+    assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+    assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+    assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+
+    // Feb 5, 2018 at 10:50 PST
+    cal.set(2018, Calendar.FEBRUARY, 5, 10, 50, 0);
+    base = cal.getTimeInMillis();
+    assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+        base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+    assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+        base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+    assertEquals("11/27/2017, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+        base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+    assertEquals("January 6, 2018", getRelativeTimeSpanString(en_US, tz,
+        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+    assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+    assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+    assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+  }
+}
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index a0d1e5a..e648e8a 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -16,18 +16,26 @@
 
 package libcore.io;
 
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.PacketSocketAddress;
+import android.system.StructTimeval;
 import android.system.StructUcred;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.InetUnixAddress;
+import java.net.NetworkInterface;
 import java.net.ServerSocket;
 import java.net.SocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.Locale;
 import junit.framework.TestCase;
 import static android.system.OsConstants.*;
@@ -44,6 +52,20 @@
     s.close();
   }
 
+  public void testFcntlInt() throws Exception {
+    File f = File.createTempFile("OsTest", "tst");
+    FileInputStream fis = null;
+    try {
+      fis = new FileInputStream(f);
+      Libcore.os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
+      int flags = Libcore.os.fcntlVoid(fis.getFD(), F_GETFD);
+      assertTrue((flags & FD_CLOEXEC) != 0);
+    } finally {
+      IoUtils.closeQuietly(fis);
+      f.delete();
+    }
+  }
+
   public void testUnixDomainSockets_in_file_system() throws Exception {
     String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
     new File(path).delete();
@@ -196,9 +218,10 @@
     fis.close();
   }
 
-  public void test_byteBufferPositions_sendto_recvfrom() throws Exception {
-    final FileDescriptor serverFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
-    Libcore.os.bind(serverFd, InetAddress.getLoopbackAddress(), 0);
+  static void checkByteBufferPositions_sendto_recvfrom(
+          int family, InetAddress loopback) throws Exception {
+    final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0);
+    Libcore.os.bind(serverFd, loopback, 0);
     Libcore.os.listen(serverFd, 5);
 
     InetSocketAddress address = (InetSocketAddress) Libcore.os.getsockname(serverFd);
@@ -232,7 +255,7 @@
 
     server.start();
 
-    FileDescriptor clientFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+    FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0);
     Libcore.os.connect(clientFd, address.getAddress(), address.getPort());
 
     final byte[] bytes = "good bye, cruel black hole with fancy distortion".getBytes(StandardCharsets.US_ASCII);
@@ -254,4 +277,146 @@
 
     Libcore.os.close(clientFd);
   }
+
+  public void test_NetlinkSocket() throws Exception {
+    FileDescriptor nlSocket = Libcore.os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+    Libcore.os.bind(nlSocket, new NetlinkSocketAddress());
+    NetlinkSocketAddress address = (NetlinkSocketAddress) Libcore.os.getsockname(nlSocket);
+    assertTrue(address.getPortId() > 0);
+    assertEquals(0, address.getGroupsMask());
+
+    NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
+    Libcore.os.connect(nlSocket, nlKernel);
+    NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Libcore.os.getpeername(nlSocket);
+    assertEquals(0, nlPeer.getPortId());
+    assertEquals(0, nlPeer.getGroupsMask());
+    Libcore.os.close(nlSocket);
+  }
+
+  public void test_PacketSocketAddress() throws Exception {
+    NetworkInterface lo = NetworkInterface.getByName("lo");
+    FileDescriptor fd = Libcore.os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
+    PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
+    Libcore.os.bind(fd, addr);
+
+    PacketSocketAddress bound = (PacketSocketAddress) Libcore.os.getsockname(fd);
+    assertEquals((short) ETH_P_IPV6, bound.sll_protocol);  // ETH_P_IPV6 is an int.
+    assertEquals(lo.getIndex(), bound.sll_ifindex);
+    assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
+    assertEquals(0, bound.sll_pkttype);
+
+    // The loopback address is ETH_ALEN bytes long and is all zeros.
+    // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
+    assertEquals(6, bound.sll_addr.length);
+    for (int i = 0; i < 6; i++) {
+      assertEquals(0, bound.sll_addr[i]);
+    }
+  }
+
+  public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
+    checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
+  }
+
+  public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
+    checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
+  }
+
+  private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
+    FileDescriptor recvFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
+    Libcore.os.bind(recvFd, loopback, 0);
+    StructTimeval tv = StructTimeval.fromMillis(20);
+    Libcore.os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+
+    InetSocketAddress to = ((InetSocketAddress) Libcore.os.getsockname(recvFd));
+    FileDescriptor sendFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
+    byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
+    int len = msg.length;
+
+    assertEquals(len, Libcore.os.sendto(sendFd, msg, 0, len, 0, to));
+    byte[] received = new byte[msg.length + 42];
+    InetSocketAddress from = new InetSocketAddress();
+    assertEquals(len, Libcore.os.recvfrom(recvFd, received, 0, received.length, 0, from));
+    assertEquals(loopback, from.getAddress());
+  }
+
+  public void test_sendtoSocketAddress_af_inet() throws Exception {
+      checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
+  }
+
+  public void test_sendtoSocketAddress_af_inet6() throws Exception {
+      checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
+  }
+
+  public void test_socketFamilies() throws Exception {
+    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+    Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
+    InetSocketAddress localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+    assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+    fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+    Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+    localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+    assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+    fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
+    Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+    localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+    assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
+    try {
+      Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
+      fail("Expected ErrnoException binding IPv4 socket to ::");
+    } catch (ErrnoException expected) {
+      assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT, expected.errno);
+    }
+  }
+
+  private static void assertArrayEquals(byte[] expected, byte[] actual) {
+    assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+               Arrays.equals(expected, actual));
+  }
+
+  private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
+          byte type, byte responseType, boolean useSendto) throws Exception {
+    int len = packet.length;
+    packet[0] = type;
+    if (useSendto) {
+      assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, to, 0));
+    } else {
+      Libcore.os.connect(fd, to, 0);
+      assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, null, 0));
+    }
+
+    int icmpId = ((InetSocketAddress) Libcore.os.getsockname(fd)).getPort();
+    byte[] received = new byte[4096];
+    InetSocketAddress srcAddress = new InetSocketAddress();
+    assertEquals(len, Libcore.os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
+    assertEquals(to, srcAddress.getAddress());
+    assertEquals(responseType, received[0]);
+    assertEquals(received[4], (byte) (icmpId >> 8));
+    assertEquals(received[5], (byte) (icmpId & 0xff));
+
+    received = Arrays.copyOf(received, len);
+    received[0] = (byte) type;
+    received[2] = received[3] = 0;  // Checksum.
+    received[4] = received[5] = 0;  // ICMP ID.
+    assertArrayEquals(packet, received);
+  }
+
+  public void test_socketPing() throws Exception {
+    final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
+    final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
+    final byte[] packet = ("\000\000\000\000" +  // ICMP type, code.
+                           "\000\000\000\003" +  // ICMP ID (== port), sequence number.
+                           "Hello myself").getBytes(StandardCharsets.US_ASCII);
+
+    FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+    InetAddress ipv6Loopback = InetAddress.getByName("::1");
+    checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
+    checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
+
+    fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+    InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
+    checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
+    checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
+  }
 }
diff --git a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
index 39472df..e2780c9 100644
--- a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
+++ b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
@@ -30,14 +30,14 @@
     new RandomAccessFile(f, "r").getFD().sync();
   }
 
-  public void test_isSocket() throws Exception {
+  public void test_isSocket$() throws Exception {
     File f = new File("/dev/null");
     FileInputStream fis = new FileInputStream(f);
-    assertFalse(fis.getFD().isSocket());
+    assertFalse(fis.getFD().isSocket$());
     fis.close();
 
     ServerSocket s = new ServerSocket();
-    assertTrue(s.getImpl$().getFD$().isSocket());
+    assertTrue(s.getImpl$().getFD$().isSocket$());
     s.close();
   }
 }
diff --git a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
index 14950ee..26de11a 100644
--- a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
@@ -67,7 +67,7 @@
     }
 
     public void testSkipInPipes() throws Exception {
-        FileDescriptor[] pipe = Libcore.os.pipe();
+        FileDescriptor[] pipe = Libcore.os.pipe2(0);
         DataFeeder feeder = new DataFeeder(pipe[1]);
         try {
             feeder.start();
diff --git a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
index afe49b7..8d99457 100644
--- a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
+++ b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
@@ -20,6 +20,8 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+
 import junit.framework.TestCase;
 import libcore.java.lang.ref.FinalizationTester;
 
@@ -73,6 +75,61 @@
             FinalizationTester.induceFinalization();
         }
     }
+
+    // http://b/19892782
+    public void testCloseRaf_sameChannelReturned() throws Exception {
+        RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+        FileChannel fileChannelBeforeClosing = raf.getChannel();
+        raf.close();
+        FileChannel fileChannelAfterClosing = raf.getChannel();
+        assertSame(fileChannelBeforeClosing, fileChannelAfterClosing);
+    }
+
+    // http://b/19892782
+    public void testCloseRaf_channelIsClosed() throws Exception {
+        RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+        FileChannel fileChannelBeforeClosing = raf.getChannel();
+        raf.close();
+        FileChannel fileChannelAfterClosing = raf.getChannel();
+        assertFalse(fileChannelBeforeClosing.isOpen());
+    }
+
+    // http://b/19892782
+    public void testCloseFileChannel_sameChannelReturned() throws Exception {
+        RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+        FileChannel fileChannelBeforeClosing = raf.getChannel();
+        fileChannelBeforeClosing.close();
+
+        FileChannel fileChannelAfterClosing = raf.getChannel();
+        assertSame(fileChannelBeforeClosing, fileChannelAfterClosing);
+    }
+
+    // http://b/19892782
+    public void testCloseFileChannel_returnedFileChannelIsClosed() throws Exception {
+        RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+        FileChannel fileChannelBeforeClosing = raf.getChannel();
+        // This should close the Raf, and previous implementations wrongly returned a new
+        // open (but useless) channel in this case.
+        fileChannelBeforeClosing.close();
+        FileChannel fileChannelAfterClosing = raf.getChannel();
+        assertFalse(fileChannelBeforeClosing.isOpen());
+    }
+
+    // http://b/19892782
+    public void testCloseRafBeforeGetChannel_returnChannelWithCloseFdAfterClose() throws Exception {
+        RandomAccessFile raf = new RandomAccessFile(file, "rw");
+        raf.close();
+        try {
+            raf.getChannel().size();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
     private void createRandomAccessFile(File file) throws Exception {
         // TODO: fix our register maps and remove this otherwise unnecessary
         // indirection! (http://b/5412580)
diff --git a/luni/src/test/java/libcore/java/lang/FloatTest.java b/luni/src/test/java/libcore/java/lang/FloatTest.java
index 92e7ae4..c25bd5c 100644
--- a/luni/src/test/java/libcore/java/lang/FloatTest.java
+++ b/luni/src/test/java/libcore/java/lang/FloatTest.java
@@ -121,4 +121,20 @@
       }
       assertEquals(f1, 0f);
     }
+
+    // Float equivalent of testParseLargestSubnormalDoublePrecision. http://b/18087920.
+    public void testParseLargestSubnormalFloatPrecision() {
+        // These are different ways of saying MIN_NORMAL.
+        assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-38"));
+        assertEquals(1.1754943508222875e-38f, Float.parseFloat("0.00011754943508222875e-34f"));
+        assertEquals(1.1754943508222875e-38f, Float.parseFloat("00000001.1754943508222875e-38f"));
+        assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.17549435082228750000e-38f"));
+        assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-0038f"));
+        assertEquals(-1.1754943508222875e-38f, Float.parseFloat("-1.1754943508222875e-38f"));
+
+        // Extra interesting values suggested as part of http://b/18087920.
+        assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573643E-54"));
+        assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573644E-54"));
+        assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573645E-54"));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java b/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
old mode 100644
new mode 100755
index d1704fb..4760c6d
--- a/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
@@ -103,7 +103,7 @@
     }
 
     private class Interrupter extends Thread {
-            Waiter waiter;
+            private final Waiter waiter;
 
             Interrupter(String name, Waiter waiter) {
                 super(name);
@@ -119,8 +119,7 @@
                 }
             }
 
-            void run_inner() {
-                waiter.spin = true;
+            private void run_inner() {
                 // System.out.println("InterruptTest: starting waiter");
                 waiter.start();
 
@@ -168,7 +167,7 @@
 
     private class Waiter extends Thread {
             Object interrupterLock = new Object();
-            Boolean spin = false;
+            volatile boolean spin = true;
 
             Waiter(String name) {
                 super(name);
@@ -188,6 +187,7 @@
                 while (spin) {
                     // We're going to get interrupted while we spin.
                 }
+
                 if (interrupted()) {
                     // System.out.println("Waiter done spinning; interrupted.");
                 } else {
@@ -196,7 +196,7 @@
                 }
 
                 synchronized (this) {
-                    Boolean sawEx = false;
+                    boolean sawEx = false;
 
                     try {
                         synchronized (interrupterLock) {
@@ -216,7 +216,7 @@
                     }
                 }
                 synchronized (this) {
-                    Boolean sawEx = false;
+                    boolean sawEx = false;
 
                     try {
                         synchronized (interrupterLock) {
@@ -236,7 +236,7 @@
                     }
                 }
                 synchronized (this) {
-                    Boolean sawEx = false;
+                    boolean sawEx = false;
 
                     try {
                         synchronized (interrupterLock) {
diff --git a/luni/src/test/java/libcore/java/lang/OldClassTest.java b/luni/src/test/java/libcore/java/lang/OldClassTest.java
index 23a42bd..f5bc787 100644
--- a/luni/src/test/java/libcore/java/lang/OldClassTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldClassTest.java
@@ -40,11 +40,6 @@
 
 @SuppressWarnings("deprecation")
 public class OldClassTest extends junit.framework.TestCase {
-
-    public static final String FILENAME =
-        OldClassTest.class.getPackage().getName().replace('.', '/') +
-        "/test#.properties";
-
     final String packageName = getClass().getPackage().getName();
     final String classNameInitError1 = packageName + ".TestClass1";
     final String classNameInitError2 = packageName + ".TestClass1B";
diff --git a/luni/src/test/java/libcore/java/lang/PackageTest.java b/luni/src/test/java/libcore/java/lang/PackageTest.java
index 6e274a0..c004e23 100644
--- a/luni/src/test/java/libcore/java/lang/PackageTest.java
+++ b/luni/src/test/java/libcore/java/lang/PackageTest.java
@@ -25,9 +25,10 @@
     private static final List<Package> packages = Arrays.asList(Package.getPackages());
 
     public void test_getAnnotations() throws Exception {
-        // Package annotations aren't supported, but pre-ICS we crashed.
-        assertEquals(0, getClass().getPackage().getAnnotations().length);
-        assertEquals(0, getClass().getPackage().getDeclaredAnnotations().length);
+        // Pre-ICS we crashed. To pass, the package-info and TestPackageAnnotation classes must be
+        // on the classpath.
+        assertEquals(1, getClass().getPackage().getAnnotations().length);
+        assertEquals(1, getClass().getPackage().getDeclaredAnnotations().length);
     }
 
     public void testGetPackage() {
diff --git a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
index 9766cef..51aed38 100644
--- a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
+++ b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
@@ -28,7 +28,7 @@
 public class ProcessBuilderTest extends AbstractResourceLeakageDetectorTestCase {
 
     private static String shell() {
-        String deviceSh = "/system/bin/sh";
+        String deviceSh = System.getenv("ANDROID_ROOT") + "/bin/sh";
         String desktopSh = "/bin/sh";
         return new File(deviceSh).exists() ? deviceSh : desktopSh;
     }
diff --git a/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java b/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java
new file mode 100644
index 0000000..7626206
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.lang;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// Used by PackageTest
+@Target(ElementType.PACKAGE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TestPackageAnnotation {}
diff --git a/luni/src/test/java/libcore/java/lang/package-info.java b/luni/src/test/java/libcore/java/lang/package-info.java
new file mode 100644
index 0000000..d916e9a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+// Used by PackageTest
+@TestPackageAnnotation
+package libcore.java.lang;
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
index b60d984..75665db 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
@@ -17,6 +17,8 @@
 package libcore.java.lang.reflect;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
 import junit.framework.TestCase;
 
 public final class FieldTest extends TestCase {
@@ -46,6 +48,56 @@
         assertFalse(f1.equals(f2));
     }
 
+    // Tests that the "synthetic" modifier is handled correctly.
+    // It's supposed to be present but not shown in toString.
+    public void testSyntheticModifier() throws NoSuchFieldException {
+        Field valuesField = Thread.State.class.getDeclaredField("$VALUES");
+        // Check that this test makes sense.
+        assertTrue(valuesField.isSynthetic());
+        assertEquals(Modifier.SYNTHETIC, valuesField.getModifiers() & Modifier.SYNTHETIC);
+        assertEquals("private static final java.lang.Thread$State[] java.lang.Thread$State.$VALUES",
+                valuesField.toString());
+    }
+
+    // Ensure that the "enum constant" bit is not returned in toString.
+    public void testEnumValueField() throws NoSuchFieldException {
+        Field blockedField = Thread.State.class.getDeclaredField("BLOCKED");
+        assertTrue(Thread.State.class.getDeclaredField("BLOCKED").isEnumConstant());
+        assertEquals("public static final", Modifier.toString(blockedField.getModifiers()));
+        assertEquals(
+                "public static final java.lang.Thread$State java.lang.Thread$State.BLOCKED",
+                blockedField.toString());
+    }
+
+    class ClassWithATransientField {
+        private transient Class<String> transientField = String.class;
+    }
+
+    // Tests that the "transient" modifier is handled correctly.
+    // The underlying constant value for it is the same as for the "varargs" method modifier.
+    // http://b/18488857
+    public void testTransientModifier() throws NoSuchFieldException {
+        Field transientField = ClassWithATransientField.class.getDeclaredField("transientField");
+        // Check that this test makes sense.
+        assertEquals(Modifier.TRANSIENT, transientField.getModifiers() & Modifier.TRANSIENT);
+        assertEquals(
+                "private transient java.lang.Class "
+                        + "libcore.java.lang.reflect.FieldTest$ClassWithATransientField"
+                        + ".transientField",
+                transientField.toString());
+    }
+
+    public void testToGenericString() throws NoSuchFieldException {
+        Field transientField = ClassWithATransientField.class.getDeclaredField("transientField");
+        // Check that this test makes sense.
+        assertEquals(Modifier.TRANSIENT, transientField.getModifiers() & Modifier.TRANSIENT);
+        assertEquals(
+                "private transient java.lang.Class<java.lang.String> "
+                        + "libcore.java.lang.reflect.FieldTest$ClassWithATransientField"
+                        + ".transientField",
+                transientField.toGenericString());
+    }
+
     static class FieldTestHelper {
         public String a;
         public Object b;
diff --git a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
index c3a436c..a3f9065 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
@@ -17,6 +17,7 @@
 package libcore.java.lang.reflect;
 
 import java.lang.reflect.Method;
+
 import junit.framework.TestCase;
 
 public final class MethodTest extends TestCase {
@@ -197,6 +198,23 @@
         assertEquals( "public java.lang.Process java.lang.Runtime.exec(java.lang.String[])"
                 + " throws java.io.IOException",
                 Runtime.class.getMethod("exec", new Class[] { String[].class }).toString());
+        // http://b/18488857
+        assertEquals(
+                "public int java.lang.String.compareTo(java.lang.Object)",
+                String.class.getMethod("compareTo", Object.class).toString());
+    }
+
+    // Tests that the "varargs" modifier is handled correctly.
+    // The underlying constant value for it is the same as for the "transient" field modifier.
+    // http://b/18488857
+    public void testVarargsModifier() throws NoSuchMethodException {
+        Method stringFormatMethod = String.class.getMethod(
+                "format", new Class[] { String.class, Object[].class });
+        assertTrue(stringFormatMethod.isVarArgs());
+        assertEquals(
+                "public static java.lang.String java.lang.String.format("
+                        + "java.lang.String,java.lang.Object[])",
+                stringFormatMethod.toString());
     }
 
     public static class MethodTestHelper {
diff --git a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
index 1bde157..0505f2f 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
@@ -100,6 +100,9 @@
     }
 
     public void test_toStringI() {
-        assertEquals("public abstract", Modifier.toString(Modifier.PUBLIC | Modifier.ABSTRACT));
+        // Note that it checks that "STRICT" is rendered as "strictfp" (for other modifiers,
+        // the displayed name is the same as the lowercase constant name).
+        assertEquals("public abstract strictfp",
+                Modifier.toString(Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STRICT));
     }
 }
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 4b656cc..8bdcf64 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -21,10 +21,14 @@
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.UnknownHostException;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 import libcore.util.SerializationTester;
 
 public class InetAddressTest extends junit.framework.TestCase {
+    private static final byte[] LOOPBACK4_BYTES = new byte[] { 127, 0, 0, 1 };
     private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
 
     private static final String[] INVALID_IPv4_NUMERIC_ADDRESSES = new String[] {
@@ -72,7 +76,7 @@
     }
 
     private static Inet6Address localhost6() throws Exception {
-        return (Inet6Address) InetAddress.getByAddress("localhost", LOOPBACK6_BYTES);
+        return (Inet6Address) InetAddress.getByAddress("ip6-localhost", LOOPBACK6_BYTES);
     }
 
     public void test_parseNumericAddress() throws Exception {
@@ -311,4 +315,84 @@
             assertEquals(resultStrings[i], result);
         }
     }
+
+    public void test_getHostNameCaches() throws Exception {
+        InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
+        assertEquals("::1", inetAddress.getHostString());
+        assertEquals("ip6-localhost", inetAddress.getHostName());
+        // getHostString() should now be different.
+        assertEquals("ip6-localhost", inetAddress.getHostString());
+    }
+
+    public void test_getByAddress_loopbackIpv4() throws Exception {
+        InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK4_BYTES);
+        assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+        assertTrue(inetAddress.isLoopbackAddress());
+    }
+
+    public void test_getByAddress_loopbackIpv6() throws Exception {
+        InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
+        assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+        assertTrue(inetAddress.isLoopbackAddress());
+    }
+
+    public void test_getByName_loopbackIpv4() throws Exception {
+        InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
+        assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+        assertTrue(inetAddress.isLoopbackAddress());
+    }
+
+    public void test_getByName_loopbackIpv6() throws Exception {
+        InetAddress inetAddress = InetAddress.getByName("::1");
+        assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+        assertTrue(inetAddress.isLoopbackAddress());
+    }
+
+    public void test_getAllByName_localhost() throws Exception {
+        InetAddress[] inetAddresses = InetAddress.getAllByName("localhost");
+        assertEquals(1, inetAddresses.length);
+        InetAddress inetAddress = inetAddresses[0];
+        assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+        assertTrue(inetAddress.isLoopbackAddress());
+    }
+
+    public void test_getAllByName_ip6_localhost() throws Exception {
+        InetAddress[] inetAddresses = InetAddress.getAllByName("ip6-localhost");
+        assertEquals(1, inetAddresses.length);
+        InetAddress inetAddress = inetAddresses[0];
+        assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+        assertTrue(inetAddress.isLoopbackAddress());
+    }
+
+    public void test_getByName_null() throws Exception {
+        InetAddress inetAddress = InetAddress.getByName("::1");
+
+        Set<InetAddress> expectedLoopbackAddresses =
+                createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
+        assertTrue(expectedLoopbackAddresses.contains(inetAddress));
+    }
+
+    public void test_getAllByName_null() throws Exception {
+        InetAddress[] inetAddresses = InetAddress.getAllByName(null);
+        assertEquals(2, inetAddresses.length);
+        Set<InetAddress> expectedLoopbackAddresses =
+                createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
+        assertEquals(expectedLoopbackAddresses, createSet(inetAddresses));
+    }
+
+    private static void assertEquals(
+        byte[] expectedAddressBytes, String expectedHostname, InetAddress actual) {
+        assertArrayEquals(expectedAddressBytes, actual.getAddress());
+        assertEquals(expectedHostname, actual.getHostName());
+
+    }
+
+    private static void assertArrayEquals(byte[] expected, byte[] actual) {
+        assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+                Arrays.equals(expected, actual));
+    }
+
+    private static Set<InetAddress> createSet(InetAddress... members) {
+        return new HashSet<InetAddress>(Arrays.asList(members));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java b/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
index 3bca8dc..d97c48a 100644
--- a/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
@@ -63,7 +63,7 @@
         }
 
         InetSocketAddress isa = new InetSocketAddress((InetAddress)null, 80);
-        assertEquals("0.0.0.0", isa.getHostName());
+        assertEquals("::", isa.getHostName());
 
         try {
             new InetSocketAddress(InetAddress.getByName("localhost"), 65536);
@@ -80,7 +80,7 @@
 
     public void test_ConstructorI() {
         InetSocketAddress isa = new  InetSocketAddress(65535);
-        assertEquals("0.0.0.0", isa.getHostName());
+        assertEquals("::", isa.getHostName());
         assertEquals(65535, isa.getPort());
 
         try {
@@ -150,6 +150,20 @@
         assertTrue(hasHostname.isUnresolved());
         assertEquals("some host", hasHostname.getHostString());
         assertEquals("some host", hasHostname.getHostName());
+
+        InetSocketAddress hasHostnameAndAddress = new InetSocketAddress(
+                InetAddress.getByAddress("some host", new byte[] { 127, 0, 0, 1 }),
+                1234);
+        assertFalse(hasHostnameAndAddress.isUnresolved());
+        assertEquals("some host", hasHostnameAndAddress.getHostString());
+        assertEquals("some host", hasHostnameAndAddress.getHostName());
+
+        // Using a host name that is actually an IP.
+        InetSocketAddress hostnameIsIp = InetSocketAddress.createUnresolved("127.0.0.1", 1234);
+        assertTrue(hostnameIsIp.isUnresolved());
+        assertEquals("127.0.0.1", hostnameIsIp.getHostString());
+        assertEquals("127.0.0.1", hostnameIsIp.getHostName());
+
         // When we don't have a hostname, whether or not we do the reverse lookup is the difference
         // between getHostString and getHostName...
         InetAddress address = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
@@ -157,4 +171,18 @@
         assertEquals("127.0.0.1", noHostname.getHostString());
         assertEquals("localhost", noHostname.getHostName());
     }
+
+    public void test_getHostString_cachingBehavior() throws Exception {
+        InetAddress inetAddress = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
+        InetSocketAddress socketAddress = new InetSocketAddress(inetAddress, 1234);
+        assertEquals("127.0.0.1", socketAddress.getHostString());
+        assertEquals("localhost", socketAddress.getHostName());
+        assertEquals("localhost", socketAddress.getHostString());
+
+        inetAddress = InetAddress.getByName("127.0.0.1");
+        socketAddress = new InetSocketAddress(inetAddress, 1234);
+        assertEquals("127.0.0.1", socketAddress.getHostString());
+        assertEquals("localhost", socketAddress.getHostName());
+        assertEquals("localhost", socketAddress.getHostString());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/net/OldSocketTest.java b/luni/src/test/java/libcore/java/net/OldSocketTest.java
index 80bee69..ded5802 100644
--- a/luni/src/test/java/libcore/java/net/OldSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldSocketTest.java
@@ -37,6 +37,7 @@
 import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.SocketChannel;
 import java.security.Permission;
+import tests.net.StuckServer;
 import tests.support.Support_Configuration;
 
 public class OldSocketTest extends OldSocketTestCase {
@@ -932,25 +933,15 @@
         }
 
         // start by validating the error checks
-        int portNumber = 0;
+
+        byte[] theBytes = { 0, 0, 0, 0 };
+        SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0);
+        SocketAddress nonConnectableAddress = new InetSocketAddress(InetAddress.getByAddress(theBytes), 0);
+        SocketAddress nonReachableAddress = new InetSocketAddress(StuckServer.UNREACHABLE_ADDRESS, 0);
+        SocketAddress invalidType = new mySocketAddress();
+
         Socket theSocket = null;
         ServerSocket serverSocket = null;
-        SocketAddress theAddress = null;
-        SocketAddress nonConnectableAddress = null;
-        SocketAddress nonReachableAddress = null;
-        SocketAddress invalidType = null;
-        // byte[] theBytes = {-1,-1,-1,-1};
-        byte[] theBytes = { 0, 0, 0, 0 };
-        theAddress = new InetSocketAddress(InetAddress.getLocalHost(),
-                portNumber);
-        nonConnectableAddress = new InetSocketAddress(InetAddress
-                .getByAddress(theBytes), portNumber);
-        nonReachableAddress = new InetSocketAddress(InetAddress
-                .getByName(Support_Configuration.ResolvedNotExistingHost),
-                portNumber);
-
-        invalidType = new mySocketAddress();
-
         try {
             theSocket = new Socket();
             theSocket.connect(null);
@@ -1165,7 +1156,7 @@
         byte[] theBytes = { 0, 0, 0, 0 };
         SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0);
         SocketAddress nonConnectableAddress = new InetSocketAddress(InetAddress.getByAddress(theBytes), 0);
-        SocketAddress nonReachableAddress = new InetSocketAddress(InetAddress.getByName(Support_Configuration.ResolvedNotExistingHost), 0);
+        SocketAddress nonReachableAddress = new InetSocketAddress(StuckServer.UNREACHABLE_ADDRESS, 0);
         SocketAddress invalidType = new mySocketAddress();
 
         Socket theSocket = null;
@@ -1251,6 +1242,12 @@
             theSocket.connect(nonReachableAddress, 200);
             theSocket.close();
             fail("No interrupted exception when connecting to address nobody listening on with short timeout 200: ");
+        } catch (ConnectException ce) {
+            // some networks will quickly reset the TCP connection attempt to this fake IP
+            assertTrue(
+                    "Wrong exception when connecting to address nobody listening on with short timeout 200: "
+                            + ce.toString(),
+                    (ce.getMessage() != null && ce.getMessage().contains("ECONNREFUSED")));
         } catch (Exception e) {
             assertTrue(
                     "Wrong exception when connecting to address nobody listening on with short timeout 200: "
@@ -1266,6 +1263,12 @@
             theSocket.connect(nonReachableAddress, 40);
             theSocket.close();
             fail("No interrupted exception when connecting to address nobody listening on with short timeout 40: ");
+        } catch (ConnectException ce) {
+            // some networks will quickly reset the TCP connection attempt to this fake IP
+            assertTrue(
+                    "Wrong exception when connecting to address nobody listening on with short timeout 40: "
+                            + ce.toString(),
+                    (ce.getMessage() != null && ce.getMessage().contains("ECONNREFUSED")));
         } catch (Exception e) {
             assertTrue(
                     "Wrong exception when connecting to address nobody listening on with short timeout 40: "
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index fb09be0..9765a45 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -31,9 +31,11 @@
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
 public class SocketTest extends junit.framework.TestCase {
     // See http://b/2980559.
@@ -353,6 +355,37 @@
         assertEquals(boundAddress.getPort(), localAddressAfterClose.getPort());
     }
 
+    public void testCloseDuringConnect() throws Exception {
+        final CountDownLatch signal = new CountDownLatch(1);
+
+        final Socket s = new Socket();
+        new Thread() {
+            @Override
+            public void run() {
+                try {
+                    // This address is reserved for documentation: should never be reachable.
+                    InetSocketAddress unreachableIp = new InetSocketAddress("192.0.2.0", 80);
+                    // This should never return.
+                    s.connect(unreachableIp, 0 /* infinite */);
+                    fail("Connect returned unexpectedly for: " + unreachableIp);
+                } catch (SocketException expected) {
+                    assertTrue(expected.getMessage().contains("Socket closed"));
+                    signal.countDown();
+                } catch (IOException e) {
+                    fail("Unexpected exception: " + e);
+                }
+            }
+        }.start();
+
+        // Wait for the connect() thread to run and start connect()
+        Thread.sleep(2000);
+
+        s.close();
+
+        boolean connectUnblocked = signal.await(2000, TimeUnit.MILLISECONDS);
+        assertTrue(connectUnblocked);
+    }
+
     static class MockServer {
         private ExecutorService executor;
         private ServerSocket serverSocket;
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index c09939f..3f831e0 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -16,7 +16,8 @@
 
 package libcore.java.net;
 
-import com.android.okhttp.HttpResponseCache;
+import com.android.okhttp.AndroidShimResponseCache;
+
 import com.google.mockwebserver.MockResponse;
 import com.google.mockwebserver.MockWebServer;
 import com.google.mockwebserver.RecordedRequest;
@@ -37,11 +38,14 @@
 import java.net.Proxy;
 import java.net.ResponseCache;
 import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.UnknownHostException;
+import java.nio.channels.SocketChannel;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -58,18 +62,18 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
-import javax.net.SocketFactory;
+import javax.net.ssl.HandshakeCompletedListener;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
-import libcore.java.security.StandardNames;
 import libcore.java.security.TestKeyStore;
 import libcore.java.util.AbstractResourceLeakageDetectorTestCase;
 import libcore.javax.net.ssl.TestSSLContext;
@@ -84,7 +88,7 @@
 public final class URLConnectionTest extends AbstractResourceLeakageDetectorTestCase {
 
     private MockWebServer server;
-    private HttpResponseCache cache;
+    private AndroidShimResponseCache cache;
     private String hostName;
 
     @Override protected void setUp() throws Exception {
@@ -674,11 +678,144 @@
     private void initResponseCache() throws IOException {
         String tmp = System.getProperty("java.io.tmpdir");
         File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
-        cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
+        cache = AndroidShimResponseCache.create(cacheDir, Integer.MAX_VALUE);
         ResponseCache.setDefault(cache);
     }
 
     /**
+     * Test Etag headers are returned correctly when a client-side cache is not installed.
+     * https://code.google.com/p/android/issues/detail?id=108949
+     */
+    public void testEtagHeaders_uncached() throws Exception {
+        final String etagValue1 = "686897696a7c876b7e";
+        final String body1 = "Response with etag 1";
+        final String etagValue2 = "686897696a7c876b7f";
+        final String body2 = "Response with etag 2";
+
+        server.enqueue(
+            new MockResponse()
+                .setBody(body1)
+                .setHeader("Content-Type", "text/plain")
+                .setHeader("Etag", etagValue1));
+        server.enqueue(
+            new MockResponse()
+                .setBody(body2)
+                .setHeader("Content-Type", "text/plain")
+                .setHeader("Etag", etagValue2));
+        server.play();
+
+        URL url = server.getUrl("/");
+        HttpURLConnection connection1 = (HttpURLConnection) url.openConnection();
+        assertEquals(etagValue1, connection1.getHeaderField("Etag"));
+        assertContent(body1, connection1);
+        connection1.disconnect();
+
+        // Discard the server-side record of the request made.
+        server.takeRequest();
+
+        HttpURLConnection connection2 = (HttpURLConnection) url.openConnection();
+        assertEquals(etagValue2, connection2.getHeaderField("Etag"));
+        assertContent(body2, connection2);
+        connection2.disconnect();
+
+        // Check the client did not cache.
+        RecordedRequest request = server.takeRequest();
+        assertNull(request.getHeader("If-None-Match"));
+    }
+
+    /**
+     * Test Etag headers are returned correctly when a client-side cache is installed and the server
+     * data is unchanged.
+     * https://code.google.com/p/android/issues/detail?id=108949
+     */
+    public void testEtagHeaders_cachedWithServerHit() throws Exception {
+        final String etagValue = "686897696a7c876b7e";
+        final String body = "Response with etag";
+
+        server.enqueue(
+            new MockResponse()
+                .setBody(body)
+                .setResponseCode(HttpURLConnection.HTTP_OK)
+                .setHeader("Content-Type", "text/plain")
+                .setHeader("Etag", etagValue));
+
+        server.enqueue(
+            new MockResponse()
+                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+        server.play();
+
+        initResponseCache();
+
+        URL url = server.getUrl("/");
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        assertEquals(etagValue, connection.getHeaderField("Etag"));
+        assertContent(body, connection);
+        connection.disconnect();
+
+        // Discard the server-side record of the request made.
+        server.takeRequest();
+
+        // Confirm the cached body is returned along with the original etag header.
+        HttpURLConnection cachedConnection = (HttpURLConnection) url.openConnection();
+        assertEquals(etagValue, cachedConnection.getHeaderField("Etag"));
+        assertContent(body, cachedConnection);
+        cachedConnection.disconnect();
+
+        // Check the client formatted the request correctly.
+        RecordedRequest request = server.takeRequest();
+        assertEquals(etagValue, request.getHeader("If-None-Match"));
+    }
+
+    /**
+     * Test Etag headers are returned correctly when a client-side cache is installed and the server
+     * data has changed.
+     * https://code.google.com/p/android/issues/detail?id=108949
+     */
+    public void testEtagHeaders_cachedWithServerMiss() throws Exception {
+        final String etagValue1 = "686897696a7c876b7e";
+        final String body1 = "Response with etag 1";
+        final String etagValue2 = "686897696a7c876b7f";
+        final String body2 = "Response with etag 2";
+
+        server.enqueue(
+            new MockResponse()
+                .setBody(body1)
+                .setResponseCode(HttpURLConnection.HTTP_OK)
+                .setHeader("Content-Type", "text/plain")
+                .setHeader("Etag", etagValue1));
+
+        server.enqueue(
+            new MockResponse()
+                .setBody(body2)
+                .setResponseCode(HttpURLConnection.HTTP_OK)
+                .setHeader("Content-Type", "text/plain")
+                .setHeader("Etag", etagValue2));
+
+        server.play();
+
+        initResponseCache();
+
+        URL url = server.getUrl("/");
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        assertEquals(etagValue1, connection.getHeaderField("Etag"));
+        assertContent(body1, connection);
+        connection.disconnect();
+
+        // Discard the server-side record of the request made.
+        server.takeRequest();
+
+        // Confirm the new body is returned along with the new etag header.
+        HttpURLConnection cachedConnection = (HttpURLConnection) url.openConnection();
+        assertEquals(etagValue2, cachedConnection.getHeaderField("Etag"));
+        assertContent(body2, cachedConnection);
+        cachedConnection.disconnect();
+
+        // Check the client formatted the request correctly.
+        RecordedRequest request = server.takeRequest();
+        assertEquals(etagValue1, request.getHeader("If-None-Match"));
+    }
+
+    /**
      * Test which headers are sent unencrypted to the HTTP proxy.
      */
     public void testProxyConnectIncludesProxyHeadersOnly()
@@ -2074,6 +2211,19 @@
         connection.disconnect();
     }
 
+    public void testLastModified() throws Exception {
+        server.enqueue(new MockResponse()
+                .addHeader("Last-Modified", "Wed, 27 Nov 2013 11:26:00 GMT")
+                .setBody("Hello"));
+        server.play();
+
+        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+        connection.connect();
+
+        assertEquals(1385551560000L, connection.getLastModified());
+        assertEquals(1385551560000L, connection.getHeaderFieldDate("Last-Modified", -1));
+    }
+
     public void testClientSendsContentLength() throws Exception {
         server.enqueue(new MockResponse().setBody("A"));
         server.play();
@@ -2185,52 +2335,107 @@
         urlConnection.getInputStream();
     }
 
-    public void testSslFallback() throws Exception {
+    public void testSslFallback_allSupportedProtocols() throws Exception {
         TestSSLContext testSSLContext = TestSSLContext.create();
 
-        // This server socket factory only supports SSLv3. This is to avoid issues due to SCSV
-        // checks. See https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
+        String[] allSupportedProtocols = { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" };
         SSLSocketFactory serverSocketFactory =
                 new LimitedProtocolsSocketFactory(
                         testSSLContext.serverContext.getSocketFactory(),
-                        "SSLv3");
-
+                        allSupportedProtocols);
         server.useHttps(serverSocketFactory, false);
         server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
-        server.enqueue(new MockResponse().setBody("This required a 2nd handshake"));
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setBody("This required fallbacks"));
         server.play();
 
         HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
-        // Keep track of the client sockets created so that we can interrogate them.
-        RecordingSocketFactory clientSocketFactory =
-                new RecordingSocketFactory(testSSLContext.clientContext.getSocketFactory());
+        // Keeps track of the client sockets created so that we can interrogate them.
+        final boolean disableFallbackScsv = true;
+        FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
+                new LimitedProtocolsSocketFactory(
+                        testSSLContext.clientContext.getSocketFactory(), allSupportedProtocols),
+                disableFallbackScsv);
         connection.setSSLSocketFactory(clientSocketFactory);
-        assertEquals("This required a 2nd handshake",
+        assertEquals("This required fallbacks",
                 readAscii(connection.getInputStream(), Integer.MAX_VALUE));
 
+        // Confirm the server accepted a single connection.
         RecordedRequest retry = server.takeRequest();
         assertEquals(0, retry.getSequenceNumber());
         assertEquals("SSLv3", retry.getSslProtocol());
 
         // Confirm the client fallback looks ok.
         List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
-        assertEquals(2, createdSockets.size());
-        SSLSocket clientSocket1 = createdSockets.get(0);
-        List<String> clientSocket1EnabledProtocols = Arrays.asList(
-                clientSocket1.getEnabledProtocols());
-        assertContains(clientSocket1EnabledProtocols, "TLSv1.2");
-        List<String> clientSocket1EnabledCiphers =
-                Arrays.asList(clientSocket1.getEnabledCipherSuites());
-        assertContainsNoneMatching(
-                clientSocket1EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK);
+        assertEquals(4, createdSockets.size());
+        TlsFallbackDisabledScsvSSLSocket clientSocket1 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
+        assertSslSocket(clientSocket1,
+                false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3");
 
-        SSLSocket clientSocket2 = createdSockets.get(1);
-        List<String> clientSocket2EnabledProtocols =
-                Arrays.asList(clientSocket2.getEnabledProtocols());
-        assertContainsNoneMatching(clientSocket2EnabledProtocols, "TLSv1.2");
-        List<String> clientSocket2EnabledCiphers =
-                Arrays.asList(clientSocket2.getEnabledCipherSuites());
-        assertContains(clientSocket2EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK);
+        TlsFallbackDisabledScsvSSLSocket clientSocket2 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
+        assertSslSocket(clientSocket2,
+                true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1", "SSLv3");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+        assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1", "SSLv3");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket4 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(3);
+        assertSslSocket(clientSocket4, true /* expectedWasFallbackScsvSet */, "SSLv3");
+    }
+
+    public void testSslFallback_defaultProtocols() throws Exception {
+        TestSSLContext testSSLContext = TestSSLContext.create();
+
+        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+        server.enqueue(new MockResponse().setBody("This required fallbacks"));
+        server.play();
+
+        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
+        // Keeps track of the client sockets created so that we can interrogate them.
+        final boolean disableFallbackScsv = true;
+        FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
+                testSSLContext.clientContext.getSocketFactory(),
+                disableFallbackScsv);
+        connection.setSSLSocketFactory(clientSocketFactory);
+        assertEquals("This required fallbacks",
+                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+        // Confirm the server accepted a single connection.
+        RecordedRequest retry = server.takeRequest();
+        assertEquals(0, retry.getSequenceNumber());
+        assertEquals("TLSv1", retry.getSslProtocol());
+
+        // Confirm the client fallback looks ok.
+        List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
+        assertEquals(3, createdSockets.size());
+        TlsFallbackDisabledScsvSSLSocket clientSocket1 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
+        assertSslSocket(clientSocket1,
+                false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket2 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
+        assertSslSocket(clientSocket2, true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1");
+
+        TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+                (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+        assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1");
+    }
+
+    private static void assertSslSocket(TlsFallbackDisabledScsvSSLSocket socket,
+            boolean expectedWasFallbackScsvSet, String... expectedEnabledProtocols) {
+        Set<String> enabledProtocols =
+                new HashSet<String>(Arrays.asList(socket.getEnabledProtocols()));
+        Set<String> expectedProtocolsSet = new HashSet<String>(Arrays.asList(expectedEnabledProtocols));
+        assertEquals(expectedProtocolsSet, enabledProtocols);
+        assertEquals(expectedWasFallbackScsvSet, socket.wasTlsFallbackScsvSet());
     }
 
     public void testInspectSslBeforeConnect() throws Exception {
@@ -2491,36 +2696,37 @@
         }
 
         @Override
-        public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+        public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
                 throws IOException {
-            return delegate.createSocket(s, host, port, autoClose);
+            return (SSLSocket) delegate.createSocket(s, host, port, autoClose);
         }
 
         @Override
-        public Socket createSocket() throws IOException {
-            return delegate.createSocket();
+        public SSLSocket createSocket() throws IOException {
+            return (SSLSocket) delegate.createSocket();
         }
 
         @Override
-        public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
-            return delegate.createSocket(host, port);
+        public SSLSocket createSocket(String host, int port)
+                throws IOException, UnknownHostException {
+            return (SSLSocket) delegate.createSocket(host, port);
         }
 
         @Override
-        public Socket createSocket(String host, int port, InetAddress localHost,
+        public SSLSocket createSocket(String host, int port, InetAddress localHost,
                 int localPort) throws IOException, UnknownHostException {
-            return delegate.createSocket(host, port, localHost, localPort);
+            return (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
         }
 
         @Override
-        public Socket createSocket(InetAddress host, int port) throws IOException {
-            return delegate.createSocket(host, port);
+        public SSLSocket createSocket(InetAddress host, int port) throws IOException {
+            return (SSLSocket) delegate.createSocket(host, port);
         }
 
         @Override
-        public Socket createSocket(InetAddress address, int port,
+        public SSLSocket createSocket(InetAddress address, int port,
                 InetAddress localAddress, int localPort) throws IOException {
-            return delegate.createSocket(address, port, localAddress, localPort);
+            return (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
         }
 
     }
@@ -2539,7 +2745,7 @@
         }
 
         @Override
-        public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+        public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
                 throws IOException {
             SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose);
             socket.setEnabledProtocols(protocols);
@@ -2547,21 +2753,22 @@
         }
 
         @Override
-        public Socket createSocket() throws IOException {
+        public SSLSocket createSocket() throws IOException {
             SSLSocket socket = (SSLSocket) delegate.createSocket();
             socket.setEnabledProtocols(protocols);
             return socket;
         }
 
         @Override
-        public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+        public SSLSocket createSocket(String host, int port)
+                throws IOException, UnknownHostException {
             SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
             socket.setEnabledProtocols(protocols);
             return socket;
         }
 
         @Override
-        public Socket createSocket(String host, int port, InetAddress localHost,
+        public SSLSocket createSocket(String host, int port, InetAddress localHost,
                 int localPort) throws IOException, UnknownHostException {
             SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
             socket.setEnabledProtocols(protocols);
@@ -2569,14 +2776,14 @@
         }
 
         @Override
-        public Socket createSocket(InetAddress host, int port) throws IOException {
+        public SSLSocket createSocket(InetAddress host, int port) throws IOException {
             SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
             socket.setEnabledProtocols(protocols);
             return socket;
         }
 
         @Override
-        public Socket createSocket(InetAddress address, int port,
+        public SSLSocket createSocket(InetAddress address, int port,
                 InetAddress localAddress, int localPort) throws IOException {
             SSLSocket socket =
                     (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
@@ -2586,58 +2793,337 @@
     }
 
     /**
-     * An SSLSocketFactory that delegates calls and keeps a record of any sockets created.
+     * An {@link javax.net.ssl.SSLSocket} that delegates all calls.
      */
-    private static class RecordingSocketFactory extends DelegatingSSLSocketFactory {
+    private static abstract class DelegatingSSLSocket extends SSLSocket {
+        protected final SSLSocket delegate;
 
+        public DelegatingSSLSocket(SSLSocket delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override public void shutdownInput() throws IOException {
+            delegate.shutdownInput();
+        }
+
+        @Override public void shutdownOutput() throws IOException {
+            delegate.shutdownOutput();
+        }
+
+        @Override public String[] getSupportedCipherSuites() {
+            return delegate.getSupportedCipherSuites();
+        }
+
+        @Override public String[] getEnabledCipherSuites() {
+            return delegate.getEnabledCipherSuites();
+        }
+
+        @Override public void setEnabledCipherSuites(String[] suites) {
+            delegate.setEnabledCipherSuites(suites);
+        }
+
+        @Override public String[] getSupportedProtocols() {
+            return delegate.getSupportedProtocols();
+        }
+
+        @Override public String[] getEnabledProtocols() {
+            return delegate.getEnabledProtocols();
+        }
+
+        @Override public void setEnabledProtocols(String[] protocols) {
+            delegate.setEnabledProtocols(protocols);
+        }
+
+        @Override public SSLSession getSession() {
+            return delegate.getSession();
+        }
+
+        @Override public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+            delegate.addHandshakeCompletedListener(listener);
+        }
+
+        @Override public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+            delegate.removeHandshakeCompletedListener(listener);
+        }
+
+        @Override public void startHandshake() throws IOException {
+            delegate.startHandshake();
+        }
+
+        @Override public void setUseClientMode(boolean mode) {
+            delegate.setUseClientMode(mode);
+        }
+
+        @Override public boolean getUseClientMode() {
+            return delegate.getUseClientMode();
+        }
+
+        @Override public void setNeedClientAuth(boolean need) {
+            delegate.setNeedClientAuth(need);
+        }
+
+        @Override public void setWantClientAuth(boolean want) {
+            delegate.setWantClientAuth(want);
+        }
+
+        @Override public boolean getNeedClientAuth() {
+            return delegate.getNeedClientAuth();
+        }
+
+        @Override public boolean getWantClientAuth() {
+            return delegate.getWantClientAuth();
+        }
+
+        @Override public void setEnableSessionCreation(boolean flag) {
+            delegate.setEnableSessionCreation(flag);
+        }
+
+        @Override public boolean getEnableSessionCreation() {
+            return delegate.getEnableSessionCreation();
+        }
+
+        @Override public SSLParameters getSSLParameters() {
+            return delegate.getSSLParameters();
+        }
+
+        @Override public void setSSLParameters(SSLParameters p) {
+            delegate.setSSLParameters(p);
+        }
+
+        @Override public void close() throws IOException {
+            delegate.close();
+        }
+
+        @Override public InetAddress getInetAddress() {
+            return delegate.getInetAddress();
+        }
+
+        @Override public InputStream getInputStream() throws IOException {
+            return delegate.getInputStream();
+        }
+
+        @Override public boolean getKeepAlive() throws SocketException {
+            return delegate.getKeepAlive();
+        }
+
+        @Override public InetAddress getLocalAddress() {
+            return delegate.getLocalAddress();
+        }
+
+        @Override public int getLocalPort() {
+            return delegate.getLocalPort();
+        }
+
+        @Override public OutputStream getOutputStream() throws IOException {
+            return delegate.getOutputStream();
+        }
+
+        @Override public int getPort() {
+            return delegate.getPort();
+        }
+
+        @Override public int getSoLinger() throws SocketException {
+            return delegate.getSoLinger();
+        }
+
+        @Override public int getReceiveBufferSize() throws SocketException {
+            return delegate.getReceiveBufferSize();
+        }
+
+        @Override public int getSendBufferSize() throws SocketException {
+            return delegate.getSendBufferSize();
+        }
+
+        @Override public int getSoTimeout() throws SocketException {
+            return delegate.getSoTimeout();
+        }
+
+        @Override public boolean getTcpNoDelay() throws SocketException {
+            return delegate.getTcpNoDelay();
+        }
+
+        @Override public void setKeepAlive(boolean keepAlive) throws SocketException {
+            delegate.setKeepAlive(keepAlive);
+        }
+
+        @Override public void setSendBufferSize(int size) throws SocketException {
+            delegate.setSendBufferSize(size);
+        }
+
+        @Override public void setReceiveBufferSize(int size) throws SocketException {
+            delegate.setReceiveBufferSize(size);
+        }
+
+        @Override public void setSoLinger(boolean on, int timeout) throws SocketException {
+            delegate.setSoLinger(on, timeout);
+        }
+
+        @Override public void setSoTimeout(int timeout) throws SocketException {
+            delegate.setSoTimeout(timeout);
+        }
+
+        @Override public void setTcpNoDelay(boolean on) throws SocketException {
+            delegate.setTcpNoDelay(on);
+        }
+
+        @Override public String toString() {
+            return delegate.toString();
+        }
+
+        @Override public SocketAddress getLocalSocketAddress() {
+            return delegate.getLocalSocketAddress();
+        }
+
+        @Override public SocketAddress getRemoteSocketAddress() {
+            return delegate.getRemoteSocketAddress();
+        }
+
+        @Override public boolean isBound() {
+            return delegate.isBound();
+        }
+
+        @Override public boolean isConnected() {
+            return delegate.isConnected();
+        }
+
+        @Override public boolean isClosed() {
+            return delegate.isClosed();
+        }
+
+        @Override public void bind(SocketAddress localAddr) throws IOException {
+            delegate.bind(localAddr);
+        }
+
+        @Override public void connect(SocketAddress remoteAddr) throws IOException {
+            delegate.connect(remoteAddr);
+        }
+
+        @Override public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
+            delegate.connect(remoteAddr, timeout);
+        }
+
+        @Override public boolean isInputShutdown() {
+            return delegate.isInputShutdown();
+        }
+
+        @Override public boolean isOutputShutdown() {
+            return delegate.isOutputShutdown();
+        }
+
+        @Override public void setReuseAddress(boolean reuse) throws SocketException {
+            delegate.setReuseAddress(reuse);
+        }
+
+        @Override public boolean getReuseAddress() throws SocketException {
+            return delegate.getReuseAddress();
+        }
+
+        @Override public void setOOBInline(boolean oobinline) throws SocketException {
+            delegate.setOOBInline(oobinline);
+        }
+
+        @Override public boolean getOOBInline() throws SocketException {
+            return delegate.getOOBInline();
+        }
+
+        @Override public void setTrafficClass(int value) throws SocketException {
+            delegate.setTrafficClass(value);
+        }
+
+        @Override public int getTrafficClass() throws SocketException {
+            return delegate.getTrafficClass();
+        }
+
+        @Override public void sendUrgentData(int value) throws IOException {
+            delegate.sendUrgentData(value);
+        }
+
+        @Override public SocketChannel getChannel() {
+            return delegate.getChannel();
+        }
+
+        @Override public void setPerformancePreferences(int connectionTime, int latency,
+                int bandwidth) {
+            delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
+        }
+    }
+
+    /**
+     * An SSLSocketFactory that delegates calls. It keeps a record of any sockets created.
+     * If {@link #disableTlsFallbackScsv} is set to {@code true} then sockets created by the
+     * delegate are wrapped with ones that will not accept the {@link #TLS_FALLBACK_SCSV} cipher,
+     * thus bypassing server-side fallback checks on platforms that support it. Unfortunately this
+     * wrapping will disable any reflection-based calls to SSLSocket from Platform.
+     */
+    private static class FallbackTestClientSocketFactory extends DelegatingSSLSocketFactory {
+        /**
+         * The cipher suite used during TLS connection fallback to indicate a fallback.
+         * See https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
+         */
+        public static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV";
+
+        private final boolean disableTlsFallbackScsv;
         private final List<SSLSocket> createdSockets = new ArrayList<SSLSocket>();
 
-        private RecordingSocketFactory(SSLSocketFactory delegate) {
+        public FallbackTestClientSocketFactory(SSLSocketFactory delegate,
+                boolean disableTlsFallbackScsv) {
             super(delegate);
+            this.disableTlsFallbackScsv = disableTlsFallbackScsv;
         }
 
-        @Override
-        public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+        @Override public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
                 throws IOException {
-            SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose);
+            SSLSocket socket = super.createSocket(s, host, port, autoClose);
+            if (disableTlsFallbackScsv) {
+                socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+            }
             createdSockets.add(socket);
             return socket;
         }
 
-        @Override
-        public Socket createSocket() throws IOException {
-            SSLSocket socket = (SSLSocket) delegate.createSocket();
+        @Override public SSLSocket createSocket() throws IOException {
+            SSLSocket socket = super.createSocket();
+            if (disableTlsFallbackScsv) {
+                socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+            }
             createdSockets.add(socket);
             return socket;
         }
 
-        @Override
-        public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
-            SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
+        @Override public SSLSocket createSocket(String host,int port) throws IOException {
+            SSLSocket socket = super.createSocket(host, port);
+            if (disableTlsFallbackScsv) {
+                socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+            }
             createdSockets.add(socket);
             return socket;
         }
 
-        @Override
-        public Socket createSocket(String host, int port, InetAddress localHost,
-                int localPort) throws IOException, UnknownHostException {
-            SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
+        @Override public SSLSocket createSocket(String host,int port, InetAddress localHost,
+                int localPort) throws IOException {
+            SSLSocket socket = super.createSocket(host, port, localHost, localPort);
+            if (disableTlsFallbackScsv) {
+                socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+            }
             createdSockets.add(socket);
             return socket;
         }
 
-        @Override
-        public Socket createSocket(InetAddress host, int port) throws IOException {
-            SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
+        @Override public SSLSocket createSocket(InetAddress host,int port) throws IOException {
+            SSLSocket socket = super.createSocket(host, port);
+            if (disableTlsFallbackScsv) {
+                socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+            }
             createdSockets.add(socket);
             return socket;
         }
 
-        @Override
-        public Socket createSocket(InetAddress address, int port,
+        @Override public SSLSocket createSocket(InetAddress address,int port,
                 InetAddress localAddress, int localPort) throws IOException {
-            SSLSocket socket =
-                    (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
+            SSLSocket socket = super.createSocket(address, port, localAddress, localPort);
+            if (disableTlsFallbackScsv) {
+                socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+            }
             createdSockets.add(socket);
             return socket;
         }
@@ -2647,4 +3133,31 @@
         }
     }
 
+    private static class TlsFallbackDisabledScsvSSLSocket extends DelegatingSSLSocket {
+
+        private boolean tlsFallbackScsvSet;
+
+        public TlsFallbackDisabledScsvSSLSocket(SSLSocket socket) {
+            super(socket);
+        }
+
+        @Override public void setEnabledCipherSuites(String[] suites) {
+            List<String> enabledCipherSuites = new ArrayList<String>(suites.length);
+            for (String suite : suites) {
+                if (suite.equals(FallbackTestClientSocketFactory.TLS_FALLBACK_SCSV)) {
+                    // Record that an attempt was made to set TLS_FALLBACK_SCSV, but don't actually
+                    // set it.
+                    tlsFallbackScsvSet = true;
+                } else {
+                    enabledCipherSuites.add(suite);
+                }
+            }
+            delegate.setEnabledCipherSuites(
+                    enabledCipherSuites.toArray(new String[enabledCipherSuites.size()]));
+        }
+
+        public boolean wasTlsFallbackScsvSet() {
+            return tlsFallbackScsvSet;
+        }
+    }
 }
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 9789197..41b434d 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
@@ -15,7 +15,6 @@
  */
 package libcore.java.nio.channels;
 
-import android.system.OsConstants;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
@@ -28,7 +27,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import junit.framework.TestCase;
-import libcore.io.Libcore;
 import tests.net.StuckServer;
 
 public class SelectorTest extends TestCase {
@@ -71,25 +69,6 @@
         }
     }
 
-    // http://b/6453247
-    // This test won't work on the host until/unless we start using libcorkscrew there.
-    // The runtime itself blocks SIGQUIT, so that doesn't cause poll(2) to EINTR directly.
-    // The EINTR is caused by the way libcorkscrew works.
-    public void testEINTR() throws Exception {
-        Selector selector = Selector.open();
-        new Thread(new Runnable() {
-            @Override public void run() {
-                try {
-                    Thread.sleep(2000);
-                    Libcore.os.kill(Libcore.os.getpid(), OsConstants.SIGQUIT);
-                } catch (Exception ex) {
-                    fail();
-                }
-            }
-        }).start();
-        assertEquals(0, selector.select());
-    }
-
     // http://code.google.com/p/android/issues/detail?id=15388
     public void testInterrupted() throws IOException {
         Selector selector = Selector.open();
diff --git a/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
index ff510e0..e9ab8ae 100644
--- a/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
+++ b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
@@ -20,8 +20,10 @@
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CoderResult;
 import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 
 public class CharsetEncoderTest extends junit.framework.TestCase {
@@ -161,4 +163,71 @@
         assertEquals(CoderResult.UNDERFLOW, cr);
         assertEquals(8, bb.position());
     }
+
+    // Discards all input. Outputs a single byte 'X' on flush.
+    private static final class MockCharset extends Charset {
+        static final Charset INSTANCE = new MockCharset();
+
+        private MockCharset() {
+            super("MockCharset", new String[0]);
+        }
+
+        public boolean contains(Charset charset) {
+            return false;
+        }
+
+        public CharsetEncoder newEncoder() {
+            return new CharsetEncoder(INSTANCE, 1.f, 1.f) {
+                protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+                    in.position(in.limit());
+                    return CoderResult.UNDERFLOW;
+                }
+
+                protected CoderResult implFlush(ByteBuffer out) {
+                    out.put((byte) 'X');
+                    return CoderResult.UNDERFLOW;
+                }
+            };
+        }
+
+        public CharsetDecoder newDecoder() {
+            return new CharsetDecoder(INSTANCE, 1.f, 1.f) {
+                protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
+                    in.position(in.limit());
+                    return CoderResult.UNDERFLOW;
+                }
+            };
+        }
+    }
+
+    // Repeated calls to flush() should not result in repeated calls to implFlush().
+    public void testFlushNotCallingImplFlushRepeatedly() {
+        CharsetEncoder e = MockCharset.INSTANCE.newEncoder();
+        ByteBuffer bb = ByteBuffer.allocate(4);
+        CoderResult cr = e.encode(CharBuffer.allocate(0), bb, true);
+        assertEquals(CoderResult.UNDERFLOW, cr);
+        cr = e.flush(bb);
+        assertEquals(CoderResult.UNDERFLOW, cr);
+        cr = e.flush(bb);
+        assertEquals(CoderResult.UNDERFLOW, cr);
+        assertEquals(1, bb.position());
+        assertEquals((byte) 'X', bb.get(0));
+        assertEquals(0x00, bb.get(1));
+        assertEquals(0x00, bb.get(2));
+        assertEquals(0x00, bb.get(3));
+    }
+
+    // http://b/19185235
+    public void testFlushWithIncompleteInput() {
+        CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
+        ByteBuffer output = ByteBuffer.allocate(10);
+        CoderResult result = encoder.encode(CharBuffer.wrap("\ud800"), output,
+                true /* endOfInput */);
+        assertTrue(result.isUnderflow());
+
+        result = encoder.flush(output);
+        assertTrue(result.isMalformed());
+        assertEquals(1, result.length());
+        assertEquals(0, output.position());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java
new file mode 100644
index 0000000..b4886d2
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.nio.charset;
+
+import java.nio.charset.Charset;
+import java.nio.charset.spi.CharsetProvider;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * This class is registered as a charset provider by the META-INF in the libcore
+ * tests jar. Since there isn't any convenient API to dynamically register and de-register
+ * charset-providers, this class allows tests to plug in a delegate that lives for the
+ * duration of the test.
+ */
+public final class SettableCharsetProvider extends CharsetProvider {
+    private static CharsetProvider delegate;
+
+    public static void setDelegate(CharsetProvider cp) {
+        delegate = cp;
+    }
+
+    public static void clearDelegate() {
+        delegate = null;
+    }
+
+    @Override
+    public Iterator<Charset> charsets() {
+        if (delegate != null) {
+            return delegate.charsets();
+        }
+
+        return Collections.emptyIterator();
+    }
+
+    @Override
+    public Charset charsetForName(String charsetName) {
+        if (delegate != null) {
+            return delegate.charsetForName(charsetName);
+        }
+
+        return null;
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
index e7fdb1f..7e08b5f 100644
--- a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
@@ -121,7 +121,10 @@
             }
             String algorithm = service.getAlgorithm();
 
-            // AndroidKeyStore is tested in CTS.
+            // Do not test AndroidKeyStore's KeyPairGenerator. It cannot be initialized without
+            // providing AndroidKeyStore-specific algorithm parameters.
+            // It's OKish not to test AndroidKeyStore's KeyPairGenerator here because it's tested
+            // by cts/tests/test/keystore.
             if ("AndroidKeyStore".equals(provider.getName())) {
                 continue;
             }
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index 994214b..0be558e 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -160,7 +160,7 @@
     public void test_Provider_Properties() throws Exception {
         /*
          * A useful reference on Provider properties
-         * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html>
+         * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
          * How to Implement a Provider in the Java &trade; Cryptography Architecture
          * </a>
          */
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 5e02f10..e546f4f 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -71,6 +71,24 @@
         }
     }
 
+    public void testSignature_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+                put("Signature.FOO SupportedKeyClasses", "None");
+            }
+        };
+
+        Security.addProvider(mockProvider);
+        try {
+            Signature s = Signature.getInstance("FOO", mockProvider);
+            s.initSign(new MockPrivateKey());
+            assertEquals(mockProvider, s.getProvider());
+        } finally {
+            Security.removeProvider(mockProvider.getName());
+        }
+    }
+
     public void testSignature_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
             throws Exception {
         Provider mockProvider = new MockProvider("MockProvider") {
diff --git a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
index e2f21e8..a3a721a 100644
--- a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
@@ -16,11 +16,13 @@
 
 package libcore.java.security.cert;
 
+import com.android.org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
 import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
-import com.android.org.bouncycastle.asn1.x509.X509Extensions;
+import com.android.org.bouncycastle.asn1.x509.Extension;
+import com.android.org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
 import com.android.org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
-import com.android.org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -28,13 +30,14 @@
 import java.io.InputStream;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import java.io.OptionalDataException;
-import java.io.StreamCorruptedException;
 import java.math.BigInteger;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.Provider;
+import java.security.PublicKey;
 import java.security.Security;
 import java.security.cert.CertPath;
 import java.security.cert.Certificate;
@@ -554,25 +557,26 @@
 
         X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
 
+        PublicKey pubKey = keyPair.getPublic();
         certGen.setSerialNumber(serial);
         certGen.setIssuerDN(issuerPrincipal);
         certGen.setNotBefore(startDate);
         certGen.setNotAfter(expiryDate);
         certGen.setSubjectDN(subjectPrincipal);
-        certGen.setPublicKey(keyPair.getPublic());
+        certGen.setPublicKey(pubKey);
         certGen.setSignatureAlgorithm("SHA1withRSA");
 
         if (issuer != null) {
-            certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
+            certGen.addExtension(Extension.authorityKeyIdentifier, false,
                     new AuthorityKeyIdentifierStructure(issuer.certificate));
         } else {
-            certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
-                    new AuthorityKeyIdentifierStructure(keyPair.getPublic()));
+            certGen.addExtension(Extension.authorityKeyIdentifier, false,
+                    new AuthorityKeyIdentifier(generatePublicKeyDigest(pubKey)));
         }
 
-        certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
-                new SubjectKeyIdentifierStructure(keyPair.getPublic()));
-        certGen.addExtension(X509Extensions.BasicConstraints, true, basicConstraints);
+        certGen.addExtension(Extension.subjectKeyIdentifier, false,
+                new SubjectKeyIdentifier(generatePublicKeyDigest(pubKey)));
+        certGen.addExtension(Extension.basicConstraints, true, basicConstraints);
 
         X509Certificate cert = certGen.generate(caKey);
 
@@ -582,4 +586,18 @@
 
         return holder;
     }
+
+    /**
+     * Generates a type 1 key identifier according to RFC 3280 4.2.1.2.
+     */
+    private static byte[] generatePublicKeyDigest(PublicKey pubKey) {
+        SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
+        MessageDigest sha1digest;
+        try {
+            sha1digest = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("SHA-1 not available");
+        }
+        return sha1digest.digest(spki.getPublicKeyData().getBytes());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
index 42de50a..1611120 100644
--- a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
@@ -256,15 +256,7 @@
 
     private void getSigAlgName(CertificateFactory f) throws Exception {
         X509CRL crlRsa = getCRL(f, CRL_RSA);
-
-        String actual = crlRsa.getSigAlgName().toUpperCase(Locale.US);
-
-        // Bouncycastle is broken
-        if ("BC".equals(f.getProvider().getName())) {
-            assertEquals("1.2.840.113549.1.1.5", actual);
-        } else {
-            assertEquals("SHA1WITHRSA", actual);
-        }
+        assertEquals("SHA1WITHRSA",  getCRL(f, CRL_RSA).getSigAlgName().toUpperCase(Locale.ROOT));
     }
 
     private void getSigAlgOID(CertificateFactory f) throws Exception {
diff --git a/luni/src/test/java/libcore/java/sql/TimestampTest.java b/luni/src/test/java/libcore/java/sql/TimestampTest.java
index 2985848..71ac8c8 100644
--- a/luni/src/test/java/libcore/java/sql/TimestampTest.java
+++ b/luni/src/test/java/libcore/java/sql/TimestampTest.java
@@ -144,4 +144,12 @@
     } catch (IllegalArgumentException expected) { }
   }
 
+  // http://b/19756610
+  public void testAsymmetricEquals() {
+    Timestamp timestamp = new Timestamp(0);
+    java.util.Date date = new java.util.Date(0);
+
+    assertTrue(date.equals(timestamp));
+    assertFalse(timestamp.equals(date));
+  }
 }
diff --git a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
index 47701c8..de2ae52 100644
--- a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
+++ b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
@@ -172,32 +172,4 @@
             // Expected exception
         }
     }
-
-    // http://code.google.com/p/android/issues/detail?id=41143
-    // This code is inherently unsafe and crazy;
-    // we're just trying to provoke native crashes!
-    public void testConcurrentBreakIteratorAccess() throws Exception {
-        final BreakIterator it = BreakIterator.getCharacterInstance();
-
-        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 < 4096; ++i) {
-                        it.setText("some example text");
-                        for (int index = it.first(); index != BreakIterator.DONE; index = it.next()) {
-                        }
-                    }
-                }
-            });
-            threads.add(t);
-        }
-
-        for (Thread t : threads) {
-            t.start();
-        }
-        for (Thread t : threads) {
-            t.join();
-        }
-    }
 }
diff --git a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
index e6933e6..0c97f34 100644
--- a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
@@ -147,17 +147,17 @@
     }
 
     // http://b/7955614
-    public void test_getZoneStrings_GMT_short_names() throws Exception {
+    public void test_getZoneStrings_Apia() throws Exception {
         String[][] array = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
         for (int i = 0; i < array.length; ++i) {
             String[] row = array[i];
-            // America/Santiago is somewhat arbitrary; we just want a zone we have to generate
+            // Pacific/Apia is somewhat arbitrary; we just want a zone we have to generate
             // "GMT" strings for the short names.
-            if (row[0].equals("America/Santiago")) {
-                assertEquals("Chile Standard Time", row[1]);
-                assertEquals("GMT-03:00", row[2]);
-                assertEquals("Chile Summer Time", row[3]);
-                assertEquals("GMT-03:00", row[4]);
+            if (row[0].equals("Pacific/Apia")) {
+                assertEquals("Apia Standard Time", row[1]);
+                assertEquals("GMT+13:00", row[2]);
+                assertEquals("Apia Daylight Time", row[3]);
+                assertEquals("GMT+14:00", row[4]);
             }
         }
     }
diff --git a/luni/src/test/java/libcore/java/text/NumberFormatTest.java b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
index 0678e96..87fe96d 100644
--- a/luni/src/test/java/libcore/java/text/NumberFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
@@ -17,6 +17,7 @@
 package libcore.java.text;
 
 import java.math.BigInteger;
+import java.math.RoundingMode;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.text.FieldPosition;
@@ -93,6 +94,21 @@
         }
     }
 
+    public void testPercentageRounding() throws Exception {
+        NumberFormat nf = NumberFormat.getPercentInstance(Locale.US);
+        assertEquals("15%", nf.format(0.149));
+        assertEquals("14%", nf.format(0.142));
+
+        nf.setRoundingMode(RoundingMode.UP);
+        assertEquals("15%", nf.format(0.142));
+
+        nf.setRoundingMode(RoundingMode.DOWN);
+        assertEquals("14%", nf.format(0.149));
+
+        nf.setMaximumFractionDigits(1);
+        assertEquals("14.9%", nf.format(0.149));
+    }
+
     // https://code.google.com/p/android/issues/detail?id=62269
     public void test_62269() throws Exception {
         NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java
index e0e1a35..2e13ad8 100644
--- a/luni/src/test/java/libcore/java/util/CalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/CalendarTest.java
@@ -263,4 +263,76 @@
         b.setTime(d);
         assertEquals(a, b);
     }
+
+    public void testCloneMakesDeepCopyOfCalendarFields() {
+        FakeCalendar c = new FakeCalendar();
+        FakeCalendar c2 = (FakeCalendar) c.clone();
+
+        assertFalse(c.getTimeZone() == c2.getTimeZone());
+        assertEquals(c.getTimeZone(), c2.getTimeZone());
+
+        // The default clone() implementation makes a deep copy of calendar
+        // fields...
+        assertFalse(c.getCalenderFields() == c2.getCalenderFields());
+        // ,,, and a shallow copy of subclass fields.
+        assertSame(c.getSubclassFields(), c2.getSubclassFields());
+    }
+
+    public static class FakeCalendar extends Calendar {
+
+        private int[] subclassFields;
+
+        public FakeCalendar() {
+            super(TimeZone.getDefault(), Locale.getDefault());
+            subclassFields = new int[12];
+        }
+
+        public int[] getCalenderFields() {
+            return fields;
+        }
+
+        public int[] getSubclassFields() {
+            return subclassFields;
+        }
+
+        @Override
+        public void add(int field, int value) {
+
+        }
+
+        @Override
+        protected void computeFields() {
+
+        }
+
+        @Override
+        protected void computeTime() {
+
+        }
+
+        @Override
+        public int getGreatestMinimum(int field) {
+            return 0;
+        }
+
+        @Override
+        public int getLeastMaximum(int field) {
+            return 0;
+        }
+
+        @Override
+        public int getMaximum(int field) {
+            return 0;
+        }
+
+        @Override
+        public int getMinimum(int field) {
+            return 0;
+        }
+
+        @Override
+        public void roll(int field, boolean increment) {
+
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/CollectionsTest.java b/luni/src/test/java/libcore/java/util/CollectionsTest.java
index 80c769e..bc73817 100644
--- a/luni/src/test/java/libcore/java/util/CollectionsTest.java
+++ b/luni/src/test/java/libcore/java/util/CollectionsTest.java
@@ -17,7 +17,10 @@
 package libcore.java.util;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.ListIterator;
@@ -94,4 +97,92 @@
         } catch (IllegalStateException expected) {
         }
     }
+
+    public void testSortFastPath_incrementsModcount() {
+        ArrayList<String> list = new ArrayList<String>(16);
+        list.add("coven");
+        list.add("asylum");
+        list.add("murder house");
+        list.add("freak show");
+
+        Iterator<String> it = list.iterator();
+        it.next();
+        Collections.sort(list);
+        try {
+            it.next();
+            fail();
+        } catch (ConcurrentModificationException expected) {
+        }
+    }
+
+    /**
+     * A value type whose {@code compareTo} method returns one of {@code 0},
+     * {@code Integer.MIN_VALUE} and {@code Integer.MAX_VALUE}.
+     */
+    static final class IntegerWithExtremeComparator
+            implements Comparable<IntegerWithExtremeComparator> {
+        private final int value;
+
+        public IntegerWithExtremeComparator(int value) {
+            this.value = value;
+        }
+
+        @Override
+        public int compareTo(IntegerWithExtremeComparator another) {
+            if (another.value == this.value) {
+                return 0;
+            } else if (another.value > this.value) {
+                return Integer.MIN_VALUE;
+            } else {
+                return Integer.MAX_VALUE;
+            }
+        }
+    }
+
+    // http://b/19749094
+    public void testBinarySearch_comparatorThatReturnsMinAndMaxValue() {
+        ArrayList<Integer> list = new ArrayList<Integer>(16);
+        list.add(4);
+        list.add(9);
+        list.add(11);
+        list.add(14);
+        list.add(16);
+
+        int index = Collections.binarySearch(list, 9, new Comparator<Integer>() {
+            @Override
+            public int compare(Integer lhs, Integer rhs) {
+                final int compare = lhs.compareTo(rhs);
+                if (compare == 0) {
+                    return 0;
+                } else if (compare < 0) {
+                    return Integer.MIN_VALUE;
+                } else {
+                    return Integer.MAX_VALUE;
+                }
+            }
+        });
+        assertEquals(1, index);
+
+        ArrayList<IntegerWithExtremeComparator> list2 =
+                new ArrayList<IntegerWithExtremeComparator>();
+        list2.add(new IntegerWithExtremeComparator(4));
+        list2.add(new IntegerWithExtremeComparator(9));
+        list2.add(new IntegerWithExtremeComparator(11));
+        list2.add(new IntegerWithExtremeComparator(14));
+        list2.add(new IntegerWithExtremeComparator(16));
+
+        assertEquals(1, Collections.binarySearch(list2, new IntegerWithExtremeComparator(9)));
+    }
+
+    public void testBinarySearch_emptyCollection() {
+        assertEquals(-1, Collections.binarySearch(new ArrayList<Integer>(), 9));
+
+        assertEquals(-1, Collections.binarySearch(new ArrayList<Integer>(), 9,
+                new Comparator<Integer>() {
+                    @Override
+                    public int compare(Integer lhs, Integer rhs) {
+                        return lhs.compareTo(rhs);
+                    }
+                }));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java
index cf2a1b6..e4c56e5 100644
--- a/luni/src/test/java/libcore/java/util/CurrencyTest.java
+++ b/luni/src/test/java/libcore/java/util/CurrencyTest.java
@@ -58,7 +58,7 @@
         assertEquals("Swiss Franc", Currency.getInstance("CHF").getDisplayName(Locale.US));
         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")));
+        assertEquals("franco svizzero", Currency.getInstance("CHF").getDisplayName(new Locale("it", "CH")));
     }
 
     public void test_getDefaultFractionDigits() throws Exception {
diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java
index c72ecd7..e1e84ab 100644
--- a/luni/src/test/java/libcore/java/util/LocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/LocaleTest.java
@@ -20,6 +20,7 @@
 import java.text.Collator;
 import java.text.DateFormat;
 import java.text.DateFormatSymbols;
+import java.text.DecimalFormatSymbols;
 import java.text.NumberFormat;
 import java.util.Calendar;
 import java.util.IllformedLocaleException;
@@ -1146,4 +1147,60 @@
         assertEquals("variant", locale.getVariant());
         assertEquals(locale, Locale.forLanguageTag(locale.toLanguageTag()));
     }
+
+    public void testArabicDigits() throws Exception {
+        // ar-DZ uses latn digits by default, but we can override that.
+        Locale ar_DZ = Locale.forLanguageTag("ar-DZ");
+        Locale ar_DZ_arab = Locale.forLanguageTag("ar-DZ-u-nu-arab");
+        Locale ar_DZ_latn = Locale.forLanguageTag("ar-DZ-u-nu-latn");
+        assertEquals('0', new DecimalFormatSymbols(ar_DZ).getZeroDigit());
+        assertEquals('\u0660', new DecimalFormatSymbols(ar_DZ_arab).getZeroDigit());
+        assertEquals('0', new DecimalFormatSymbols(ar_DZ_latn).getZeroDigit());
+
+        // ar-EG uses arab digits by default, but we can override that.
+        Locale ar_EG = Locale.forLanguageTag("ar-EG");
+        Locale ar_EG_arab = Locale.forLanguageTag("ar-EG-u-nu-arab");
+        Locale ar_EG_latn = Locale.forLanguageTag("ar-EG-u-nu-latn");
+        assertEquals('\u0660', new DecimalFormatSymbols(ar_EG).getZeroDigit());
+        assertEquals('\u0660', new DecimalFormatSymbols(ar_EG_arab).getZeroDigit());
+        assertEquals('0', new DecimalFormatSymbols(ar_EG_latn).getZeroDigit());
+    }
+
+    public void testDefaultLocale() throws Exception {
+        final String userLanguage = System.getProperty("user.language", "");
+        final String userRegion = System.getProperty("user.region", "");
+        final String userLocale = System.getProperty("user.locale", "");
+        try {
+            // Assert that user.locale gets priority.
+            System.setUnchangeableSystemProperty("user.locale", "de-DE");
+            System.setUnchangeableSystemProperty("user.language", "en");
+            System.setUnchangeableSystemProperty("user.region", "US");
+
+            Locale l = Locale.getDefaultLocaleFromSystemProperties();
+            assertEquals("de", l.getLanguage());
+            assertEquals("DE", l.getCountry());
+
+            // Assert that it's parsed as a full language tag.
+            System.setUnchangeableSystemProperty("user.locale", "de-Latn-DE");
+            System.setUnchangeableSystemProperty("user.language", "en");
+            System.setUnchangeableSystemProperty("user.region", "US");
+
+            l = Locale.getDefaultLocaleFromSystemProperties();
+            assertEquals("de", l.getLanguage());
+            assertEquals("DE", l.getCountry());
+            assertEquals("Latn", l.getScript());
+
+            // Assert that we use "und" if we're faced with a bad language tag, and
+            // that we don't end up with a null default locale or an exception.
+            System.setUnchangeableSystemProperty("user.locale", "dexx-Latn-DE");
+
+            l = Locale.getDefaultLocaleFromSystemProperties();
+            assertEquals("und", l.getLanguage());
+            assertEquals("DE", l.getCountry());
+        } finally {
+            System.setUnchangeableSystemProperty("user.language", userLanguage);
+            System.setUnchangeableSystemProperty("user.region", userRegion);
+            System.setUnchangeableSystemProperty("user.locale", userLocale);
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
index ecf2e5f..7a5fc4a 100644
--- a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
@@ -108,7 +108,7 @@
         TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
         assertEquals("Pacific Daylight Time", tz.getDisplayName(true,  TimeZone.LONG, Locale.US));
         assertEquals("Pacific Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.UK));
-        assertEquals("heure avanc\u00e9e du Pacifique",
+        assertEquals("heure d’été du Pacifique",
                 tz.getDisplayName(true,  TimeZone.LONG, Locale.FRANCE));
         assertEquals("heure normale du Pacifique nord-américain",
                 tz.getDisplayName(false, TimeZone.LONG, Locale.FRANCE));
@@ -123,18 +123,6 @@
         assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.FRANCE));
         assertEquals("GMT-08:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
         assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
-
-        // The RI behavior mentioned above does not appear to be because "PST" is a legacy
-        // three-character timezone supported by the RI: it happens for "Asia/Tehran"/"IRST" too
-        // (IRST is not a legacy code). The RI may just use a different dataset that has "PST" /
-        // "IRST" as valid translations (even for scripts like Chinese).
-        TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran");
-        assertEquals("Iran Summer Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.UK));
-        assertEquals("Iran Daylight Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.US));
-        assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.UK));
-        assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.US));
-        assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
-        assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
     }
 
     public void test_getID() {
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 1ca950c..68e9109 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -73,6 +73,12 @@
         assertFalse(tz.inDaylightTime(date));
     }
 
+    public void testGetDisplayNameShort_nonHourOffsets() {
+        TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran");
+        assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
+        assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
+    }
+
     public void testPreHistoricOffsets() throws Exception {
         // "Africa/Bissau" has just a few transitions and hasn't changed in a long time.
         // 1912-01-01 00:02:19-0100 ... 1912-01-01 00:02:20-0100
@@ -256,12 +262,12 @@
     }
 
     // http://b/7955614
-    public void test_getDisplayName_GMT_short_names() throws Exception {
-        TimeZone tz = TimeZone.getTimeZone("America/Santiago");
-        assertEquals("Chile Summer Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
-        assertEquals("Chile Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
-        assertEquals("GMT-03:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.US));
-        assertEquals("GMT-03:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.US));
+    public void testApia() throws Exception {
+        TimeZone tz = TimeZone.getTimeZone("Pacific/Apia");
+        assertEquals("Apia Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
+        assertEquals("Apia Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
+        assertEquals("GMT+14:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.US));
+        assertEquals("GMT+13:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.US));
     }
 
     private static boolean isGmtString(String s) {
@@ -290,4 +296,32 @@
         }
       }
     }
+
+    // http://b/18839557
+    public void testOverflowing32BitUnixDates() {
+        final TimeZone tz = TimeZone.getTimeZone("America/New_York");
+
+        // This timezone didn't have any daylight savings prior to 1917 and this
+        // date is sometime in 1901.
+        assertFalse(tz.inDaylightTime(new Date(-2206292400000L)));
+        assertEquals(-18000000, tz.getOffset(-2206292400000L));
+
+        // Nov 30th 2039, no daylight savings as per current rules.
+        assertFalse(tz.inDaylightTime(new Date(2206292400000L)));
+        assertEquals(-18000000, tz.getOffset(2206292400000L));
+    }
+
+    public void testTimeZoneIDLocalization() {
+        Locale defaultLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(new Locale("en"));
+            TimeZone en_timezone = TimeZone.getTimeZone("GMT+09:00");
+            Locale.setDefault(new Locale("ar"));
+            TimeZone ar_timezone = TimeZone.getTimeZone("GMT+09:00");
+
+            assertEquals(en_timezone.getID(), ar_timezone.getID());
+        } finally {
+            Locale.setDefault(defaultLocale);
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
index e5a6cd8..9496ad0 100644
--- a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
+++ b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
@@ -169,6 +169,10 @@
         assertThrowsOnInit("Modified_SF_EntryAttributes.jar");
     }
 
+    public void testJarSigning_removedEntry() throws Exception {
+        assertThrowsOnInit("removed.jar");
+    }
+
     private void assertThrowsOnInit(String name) throws Exception {
         Support_Resources.copyFile(resources, null,  name);
         try {
diff --git a/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java
new file mode 100644
index 0000000..9e049c0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java
@@ -0,0 +1,548 @@
+/*
+ * 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.util.zip;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+
+import tests.support.resource.Support_Resources;
+
+public abstract class AbstractZipFileTest extends TestCase {
+    /**
+     * Exercise Inflater's ability to refill the zlib's input buffer. As of this
+     * writing, this buffer's max size is 64KiB compressed bytes. We'll write a
+     * full megabyte of uncompressed data, which should be sufficient to exhaust
+     * the buffer. http://b/issue?id=2734751
+     */
+    public void testInflatingFilesRequiringZipRefill() throws IOException {
+        int originalSize = 1024 * 1024;
+        byte[] readBuffer = new byte[8192];
+        final File f = createTemporaryZipFile();
+        writeEntries(createZipOutputStream(f), 1, originalSize, false /* setEntrySize */);
+        ZipFile zipFile = new ZipFile(f);
+        for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+            ZipEntry zipEntry = e.nextElement();
+            assertTrue("This test needs >64 KiB of compressed data to exercise Inflater",
+                    zipEntry.getCompressedSize() > (64 * 1024));
+            InputStream is = zipFile.getInputStream(zipEntry);
+            while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
+            is.close();
+        }
+        zipFile.close();
+    }
+
+    private static void replaceBytes(byte[] buffer, byte[] original, byte[] replacement) {
+        // Gotcha here: original and replacement must be the same length
+        assertEquals(original.length, replacement.length);
+        boolean found;
+        for(int i=0; i < buffer.length - original.length; i++) {
+            found = false;
+            if (buffer[i] == original[0]) {
+                found = true;
+                for (int j=0; j < original.length; j++) {
+                    if (buffer[i+j] != original[j]) {
+                        found = false;
+                        break;
+                    }
+                }
+            }
+            if (found) {
+                for (int j=0; j < original.length; j++) {
+                    buffer[i+j] = replacement[j];
+                }
+            }
+        }
+    }
+
+    private static void writeBytes(File f, byte[] bytes) throws IOException {
+        FileOutputStream out = new FileOutputStream(f);
+        out.write(bytes);
+        out.close();
+    }
+
+    /**
+     * Make sure we don't fail silently for duplicate entries.
+     * b/8219321
+     */
+    public void testDuplicateEntries() throws Exception {
+        String name1 = "test_file_name1";
+        String name2 = "test_file_name2";
+
+        // Create the good zip file.
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ZipOutputStream out = createZipOutputStream(baos);
+        out.putNextEntry(new ZipEntry(name2));
+        out.closeEntry();
+        out.putNextEntry(new ZipEntry(name1));
+        out.closeEntry();
+        out.close();
+
+        // Rewrite one of the filenames.
+        byte[] buffer = baos.toByteArray();
+        replaceBytes(buffer, name2.getBytes(), name1.getBytes());
+
+        // Write the result to a file.
+        File badZip = createTemporaryZipFile();
+        writeBytes(badZip, buffer);
+
+        // Check that we refuse to load the modified file.
+        try {
+            ZipFile bad = new ZipFile(badZip);
+            fail();
+        } catch (ZipException expected) {
+        }
+    }
+
+    /**
+     * 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 = createZipOutputStream(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);
+        zip.close();
+    }
+
+    public void testInflatingStreamsRequiringZipRefill() throws IOException {
+        int originalSize = 1024 * 1024;
+        byte[] readBuffer = new byte[8192];
+        final File f = createTemporaryZipFile();
+        writeEntries(createZipOutputStream(f), 1, originalSize, false /* setEntrySize */);
+
+        ZipInputStream in = new ZipInputStream(new FileInputStream(f));
+        while (in.getNextEntry() != null) {
+            while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
+        }
+        in.close();
+    }
+
+    public void testZipFileWithLotsOfEntries() throws IOException {
+        int expectedEntryCount = 64*1024 - 1;
+        final File f = createTemporaryZipFile();
+        writeEntries(createZipOutputStream(f), expectedEntryCount, 0, false /* setEntrySize */);
+        ZipFile zipFile = new ZipFile(f);
+        int entryCount = 0;
+        for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+            ZipEntry zipEntry = e.nextElement();
+            ++entryCount;
+        }
+        assertEquals(expectedEntryCount, entryCount);
+        zipFile.close();
+    }
+
+    // http://code.google.com/p/android/issues/detail?id=36187
+    public void testZipFileLargerThan2GiB() throws IOException {
+        if (false) { // TODO: this test requires too much time and too much disk space!
+            final File f = createTemporaryZipFile();
+            writeEntries(createZipOutputStream(f), 1024, 3*1024*1024, false /* setEntrySize */);
+            ZipFile zipFile = new ZipFile(f);
+            int entryCount = 0;
+            for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+                e.nextElement();
+                ++entryCount;
+            }
+            assertEquals(1024, entryCount);
+            zipFile.close();
+        }
+    }
+
+    /**
+     * Compresses the given number of files, each of the given size, into a .zip archive.
+     */
+    protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize,
+                                boolean setEntrySize)
+            throws IOException {
+        byte[] writeBuffer = new byte[8192];
+        Random random = new Random();
+        try {
+            for (int entry = 0; entry < entryCount; ++entry) {
+                ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+                if (setEntrySize) {
+                    ze.setSize(entrySize);
+                }
+                out.putNextEntry(ze);
+
+                for (long i = 0; i < entrySize; i += writeBuffer.length) {
+                    random.nextBytes(writeBuffer);
+                    int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+                    out.write(writeBuffer, 0, byteCount);
+                }
+
+                out.closeEntry();
+            }
+        } finally {
+            out.close();
+        }
+    }
+
+    static File createTemporaryZipFile() throws IOException {
+        File result = File.createTempFile("ZipFileTest", ".zip");
+        result.deleteOnExit();
+        return result;
+    }
+
+    private ZipOutputStream createZipOutputStream(File f) throws IOException {
+        return createZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
+    }
+
+    protected abstract ZipOutputStream createZipOutputStream(OutputStream wrapped);
+
+    public void testSTORED() throws IOException {
+        ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
+        CRC32 crc = new CRC32();
+
+        // Missing CRC, size, and compressed size => failure.
+        try {
+            ZipEntry ze = new ZipEntry("a");
+            ze.setMethod(ZipEntry.STORED);
+            out.putNextEntry(ze);
+            fail();
+        } catch (ZipException expected) {
+        }
+
+        // Missing CRC and compressed size => failure.
+        try {
+            ZipEntry ze = new ZipEntry("a");
+            ze.setMethod(ZipEntry.STORED);
+            ze.setSize(0);
+            out.putNextEntry(ze);
+            fail();
+        } catch (ZipException expected) {
+        }
+
+        // Missing CRC and size => failure.
+        try {
+            ZipEntry ze = new ZipEntry("a");
+            ze.setMethod(ZipEntry.STORED);
+            ze.setSize(0);
+            ze.setCompressedSize(0);
+            out.putNextEntry(ze);
+            fail();
+        } catch (ZipException expected) {
+        }
+
+        // Missing size and compressed size => failure.
+        try {
+            ZipEntry ze = new ZipEntry("a");
+            ze.setMethod(ZipEntry.STORED);
+            ze.setCrc(crc.getValue());
+            out.putNextEntry(ze);
+            fail();
+        } catch (ZipException expected) {
+        }
+
+        // Missing size is copied from compressed size.
+        {
+            ZipEntry ze = new ZipEntry("okay1");
+            ze.setMethod(ZipEntry.STORED);
+            ze.setCrc(crc.getValue());
+
+            assertEquals(-1, ze.getSize());
+            assertEquals(-1, ze.getCompressedSize());
+
+            ze.setCompressedSize(0);
+
+            assertEquals(-1, ze.getSize());
+            assertEquals(0, ze.getCompressedSize());
+
+            out.putNextEntry(ze);
+
+            assertEquals(0, ze.getSize());
+            assertEquals(0, ze.getCompressedSize());
+        }
+
+        // Missing compressed size is copied from size.
+        {
+            ZipEntry ze = new ZipEntry("okay2");
+            ze.setMethod(ZipEntry.STORED);
+            ze.setCrc(crc.getValue());
+
+            assertEquals(-1, ze.getSize());
+            assertEquals(-1, ze.getCompressedSize());
+
+            ze.setSize(0);
+
+            assertEquals(0, ze.getSize());
+            assertEquals(-1, ze.getCompressedSize());
+
+            out.putNextEntry(ze);
+
+            assertEquals(0, ze.getSize());
+            assertEquals(0, ze.getCompressedSize());
+        }
+
+        // Mismatched size and compressed size => failure.
+        try {
+            ZipEntry ze = new ZipEntry("a");
+            ze.setMethod(ZipEntry.STORED);
+            ze.setCrc(crc.getValue());
+            ze.setCompressedSize(1);
+            ze.setSize(0);
+            out.putNextEntry(ze);
+            fail();
+        } catch (ZipException expected) {
+        }
+
+        // Everything present => success.
+        ZipEntry ze = new ZipEntry("okay");
+        ze.setMethod(ZipEntry.STORED);
+        ze.setCrc(crc.getValue());
+        ze.setSize(0);
+        ze.setCompressedSize(0);
+        out.putNextEntry(ze);
+
+        out.close();
+    }
+
+    private String makeString(int count, String ch) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < count; ++i) {
+            sb.append(ch);
+        }
+        return sb.toString();
+    }
+
+    public void testComments() throws Exception {
+        String expectedFileComment = "1 \u0666 2";
+        String expectedEntryComment = "a \u0666 b";
+
+        File file = createTemporaryZipFile();
+        ZipOutputStream out = createZipOutputStream(file);
+
+        // Is file comment length checking done on bytes or characters? (Should be bytes.)
+        out.setComment(null);
+        out.setComment(makeString(0xffff, "a"));
+        try {
+            out.setComment(makeString(0xffff + 1, "a"));
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            out.setComment(makeString(0xffff, "\u0666"));
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        ZipEntry ze = new ZipEntry("a");
+
+        // Is entry comment length checking done on bytes or characters? (Should be bytes.)
+        ze.setComment(null);
+        ze.setComment(makeString(0xffff, "a"));
+        try {
+            ze.setComment(makeString(0xffff + 1, "a"));
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            ze.setComment(makeString(0xffff, "\u0666"));
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        ze.setComment(expectedEntryComment);
+        out.putNextEntry(ze);
+        out.closeEntry();
+
+        out.setComment(expectedFileComment);
+        out.close();
+
+        ZipFile zipFile = new ZipFile(file);
+        assertEquals(expectedFileComment, zipFile.getComment());
+        assertEquals(expectedEntryComment, zipFile.getEntry("a").getComment());
+        zipFile.close();
+    }
+
+    public void test_getComment_unset() throws Exception {
+        File file = createTemporaryZipFile();
+        ZipOutputStream out = createZipOutputStream(file);
+        ZipEntry ze = new ZipEntry("test entry");
+        ze.setComment("per-entry comment");
+        out.putNextEntry(ze);
+        out.close();
+
+        ZipFile zipFile = new ZipFile(file);
+        assertEquals(null, zipFile.getComment());
+    }
+
+    // https://code.google.com/p/android/issues/detail?id=58465
+    public void test_NUL_in_filename() throws Exception {
+        File file = createTemporaryZipFile();
+
+        // We allow creation of a ZipEntry whose name contains a NUL byte,
+        // mainly because it's not likely to happen by accident and it's useful for testing.
+        ZipOutputStream out = createZipOutputStream(file);
+        out.putNextEntry(new ZipEntry("hello"));
+        out.putNextEntry(new ZipEntry("hello\u0000"));
+        out.close();
+
+        // But you can't open a ZIP file containing such an entry, because we reject it
+        // when we find it in the central directory.
+        try {
+            ZipFile zipFile = new ZipFile(file);
+            fail();
+        } catch (ZipException expected) {
+        }
+    }
+
+    public void testCrc() throws IOException {
+        ZipEntry ze = new ZipEntry("test");
+        ze.setMethod(ZipEntry.STORED);
+        ze.setSize(4);
+
+        // setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits).
+        try {
+            ze.setCrc(-1);
+        } catch (IllegalArgumentException expected) {
+        }
+
+        // You can set the CRC32 to 0xffffffff if you're slightly more careful though...
+        ze.setCrc(0xffffffffL);
+        assertEquals(0xffffffffL, ze.getCrc());
+
+        // And it actually works, even though we use -1L to mean "no CRC set"...
+        ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
+        out.putNextEntry(ze);
+        out.write(-1);
+        out.write(-1);
+        out.write(-1);
+        out.write(-1);
+        out.closeEntry();
+        out.close();
+    }
+
+    /**
+     * RI does not allow reading of an empty zip using a {@link ZipFile}.
+     */
+    public void testConstructorFailsWhenReadingEmptyZipArchive() throws IOException {
+
+        File resources = Support_Resources.createTempFolder();
+        File emptyZip = Support_Resources.copyFile(
+                resources, "java/util/zip", "EmptyArchive.zip");
+
+        try {
+            // The following should fail with an exception but if it doesn't then we need to clean
+            // up the resource so we need a reference to it.
+            ZipFile zipFile = new ZipFile(emptyZip);
+
+            // Clean up the resource.
+            try {
+                zipFile.close();
+            } catch (Exception e) {
+                // Ignore
+            }
+            fail();
+        } catch (ZipException expected) {
+            // expected
+        }
+    }
+
+    // Demonstrates http://b/18644314 : Zip entry names are relative to the point of
+    // extraction and can contain relative paths "../" and "./".
+    //
+    // It is left to callers of the API to perform any validation / santization to
+    // ensure that files are not written outside of the destination directory, where that
+    // is a concern.
+    public void testArchivesWithRelativePaths() throws IOException {
+        String[] entryNames = {
+                "../",
+                "../foo.bar",
+                "foo/../../",
+                "foo/../../bar.baz"
+        };
+
+        File zip = createTemporaryZipFile();
+        ZipOutputStream out = createZipOutputStream(zip);
+
+        try {
+            byte[] entryData = new byte[1024];
+            for (String entryName : entryNames) {
+                ZipEntry ze = new ZipEntry(entryName);
+                out.putNextEntry(ze);
+                out.write(entryData);
+                out.closeEntry();
+            }
+        } finally {
+            out.close();
+        }
+
+        ZipFile zf = new ZipFile(zip, ZipFile.OPEN_READ);
+        Enumeration<? extends ZipEntry> entries = zf.entries();
+        Set<String> entryNamesFromFile = new HashSet<>();
+        while (entries.hasMoreElements()) {
+            ZipEntry ze = entries.nextElement();
+            entryNamesFromFile.add(ze.getName());
+        }
+
+        zf.close();
+
+        for (String entryName : entryNames) {
+            assertTrue(entryNamesFromFile.contains(entryName));
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
index 30aa7f3..1dfa775 100644
--- a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
@@ -82,4 +82,26 @@
         assertTrue(totalDeflated > 0); // the deflated form should be non-empty
         assertEquals(0, totalInflated);
     }
+
+    public void testDeflaterCounts() throws Exception {
+        deflater.setInput(new byte[] { 1, 2, 3 });
+        assertEquals(11, deflater.deflate(compressed, 0, compressed.length, Deflater.FULL_FLUSH));
+        assertEquals(3, deflater.getBytesRead());
+        assertEquals(3, deflater.getTotalIn());
+        assertEquals(11, deflater.getBytesWritten());
+        assertEquals(11, deflater.getTotalOut());
+
+        deflater.setInput(new byte[] { 1, 2, 3 });
+        assertEquals(9, deflater.deflate(compressed, 0, compressed.length, Deflater.FULL_FLUSH));
+        assertEquals(6, deflater.getBytesRead());
+        assertEquals(6, deflater.getTotalIn());
+        assertEquals(20, deflater.getBytesWritten());
+
+
+        deflater.reset();
+        assertEquals(0, deflater.getBytesRead());
+        assertEquals(0, deflater.getBytesWritten());
+        assertEquals(0, deflater.getTotalIn());
+        assertEquals(0, deflater.getTotalOut());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
index 494520a..5813753 100644
--- a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
@@ -35,14 +35,46 @@
 public final class GZIPInputStreamTest extends TestCase {
 
     private static final byte[] HELLO_WORLD_GZIPPED = new byte[] {
-        31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49,
-        47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0
+        31, -117, 8, 0, 0, 0, 0, 0, 0, 0,  // 10 byte header
+        -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0  // data
+    };
+
+    /**
+     * This is the same as the above, except that the 4th header byte is 2 (FHCRC flag)
+     * and the 2 bytes after the header make up the CRC.
+     *
+     * Constructed manually because none of the commonly used tools appear to emit header CRCs.
+     */
+    private static final byte[] HELLO_WORLD_GZIPPED_WITH_HEADER_CRC = new byte[] {
+        31, -117, 8, 2, 0, 0, 0, 0, 0, 0,  // 10 byte header
+        29, 38, // 2 byte CRC.
+        -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0  // data
+    };
+
+    /*(
+     * This is the same as {@code HELLO_WORLD_GZIPPED} except that the 4th header byte is 4
+     * (FEXTRA flag) and that the 8 bytes after the header make up the extra.
+     *
+     * Constructed manually because none of the commonly used tools appear to emit header CRCs.
+     */
+    private static final byte[] HELLO_WORLD_GZIPPED_WITH_EXTRA = new byte[] {
+        31, -117, 8, 4, 0, 0, 0, 0, 0, 0,  // 10 byte header
+        6, 0, 4, 2, 4, 2, 4, 2,  // 2 byte extra length + 6 byte extra.
+        -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0  // data
     };
 
     public void testShortMessage() throws IOException {
         assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED), "UTF-8"));
     }
 
+    public void testShortMessageWithCrc() throws IOException {
+        assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED_WITH_HEADER_CRC), "UTF-8"));
+    }
+
+    public void testShortMessageWithHeaderExtra() throws IOException {
+        assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED_WITH_EXTRA), "UTF-8"));
+    }
+
     public void testLongMessage() throws IOException {
         byte[] data = new byte[1024 * 1024];
         new Random().nextBytes(data);
diff --git a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
index 158b3e9..cce08f3 100644
--- a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
@@ -122,4 +122,35 @@
         adler32.update(bytes);
         return (int) adler32.getValue();
     }
+
+    public void testInflaterCounts() throws Exception {
+        Inflater inflater = new Inflater();
+
+        byte[] decompressed = new byte[32];
+        byte[] compressed = deflate(new byte[] { 1, 2, 3}, null);
+        assertEquals(11, compressed.length);
+
+        // Feed in bytes [0, 5) to the first iteration.
+        inflater.setInput(compressed, 0, 5);
+        inflater.inflate(decompressed, 0, decompressed.length);
+        assertEquals(5, inflater.getBytesRead());
+        assertEquals(5, inflater.getTotalIn());
+        assertEquals(2, inflater.getBytesWritten());
+        assertEquals(2, inflater.getTotalOut());
+
+        // Feed in bytes [5, 11) to the second iteration.
+        assertEquals(true, inflater.needsInput());
+        inflater.setInput(compressed, 5, 6);
+        assertEquals(1, inflater.inflate(decompressed, 0, decompressed.length));
+        assertEquals(11, inflater.getBytesRead());
+        assertEquals(11, inflater.getTotalIn());
+        assertEquals(3, inflater.getBytesWritten());
+        assertEquals(3, inflater.getTotalOut());
+
+        inflater.reset();
+        assertEquals(0, inflater.getBytesRead());
+        assertEquals(0, inflater.getTotalIn());
+        assertEquals(0, inflater.getBytesWritten());
+        assertEquals(0, inflater.getTotalOut());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
new file mode 100644
index 0000000..1b733f9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.zip;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public final class Zip64FileTest extends AbstractZipFileTest {
+    @Override
+    protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
+        return new ZipOutputStream(wrapped, true /* forceZip64 */);
+    }
+
+    public void testZip64Support_largeNumberOfEntries() throws IOException {
+        final File file = createZipFile(65550, 2, false /* setEntrySize */);
+        ZipFile zf = null;
+        try {
+            zf = new ZipFile(file);
+            assertEquals(65550, zf.size());
+
+            Enumeration<? extends ZipEntry> entries = zf.entries();
+            assertTrue(entries.hasMoreElements());
+            ZipEntry ze = entries.nextElement();
+            assertEquals(2, ze.getSize());
+        } finally {
+            if (zf != null) {
+                zf.close();
+            }
+        }
+    }
+
+    public void testZip64Support_totalLargerThan4G() throws IOException {
+        final File file = createZipFile(5, 1073741824L, false /* setEntrySize */);
+        ZipFile zf = null;
+        try {
+            zf = new ZipFile(file);
+            assertEquals(5, zf.size());
+            Enumeration<? extends ZipEntry> entries = zf.entries();
+            assertTrue(entries.hasMoreElements());
+            ZipEntry ze = entries.nextElement();
+            assertEquals(1073741824L, ze.getSize());
+        } finally {
+            if (zf != null) {
+                zf.close();
+            }
+        }
+    }
+
+    public void testZip64Support_hugeEntry() throws IOException {
+        try {
+            createZipFile(1, 4294967410L, false /* setEntrySize */);
+            fail();
+        } catch (IOException expected) {
+        }
+
+        final File file = createZipFile(1, 4294967410L, true /* setEntrySize */);
+        ZipFile zf = null;
+        try {
+            zf = new ZipFile(file);
+            assertEquals(1, zf.size());
+            Enumeration<? extends ZipEntry> entries = zf.entries();
+            assertTrue(entries.hasMoreElements());
+            ZipEntry ze = entries.nextElement();
+            assertEquals(4294967410L, ze.getSize());
+        } finally {
+            if (zf != null) {
+                zf.close();
+            }
+        }
+    }
+
+    private File createZipFile(int numEntries, long entrySize, boolean setEntrySize)
+            throws IOException {
+        File file = createTemporaryZipFile();
+        // Don't force a 64 bit zip file to test that our heuristics work.
+        ZipOutputStream os = new ZipOutputStream(
+                new BufferedOutputStream(new FileOutputStream(file)));
+        writeEntries(os, numEntries, entrySize, setEntrySize);
+        return file;
+    }
+}
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 550ddfb..c7667b9 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
@@ -25,6 +25,7 @@
 import java.util.List;
 import java.util.jar.JarEntry;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
@@ -132,6 +133,7 @@
     File f = createTemporaryZipFile();
     ZipOutputStream out = createZipOutputStream(f);
     ZipEntry ze = new ZipEntry("x");
+    ze.setSize(0);
     ze.setExtra(maxLengthExtra);
     out.putNextEntry(ze);
     out.closeEntry();
@@ -143,6 +145,25 @@
     zipFile.close();
   }
 
+  public void testMaxLengthExtra_zip64() throws Exception {
+    // Not quite the max length (65535), but large enough that there's no space
+    // for the zip64 extended info header.
+    byte[] maxLengthExtra = new byte[65530];
+
+    File f = createTemporaryZipFile();
+    ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)),
+            true /* forceZip64 */);
+    ZipEntry ze = new ZipEntry("x");
+
+    ze.setExtra(maxLengthExtra);
+    try {
+      out.putNextEntry(ze);
+      fail();
+    } catch (ZipException expected) {
+    }
+  }
+
+
   public void testTooLongComment() throws Exception {
     String tooLongComment = makeString(65536, "z");
     ZipEntry ze = new ZipEntry("x");
@@ -176,7 +197,17 @@
 
     File f = createTemporaryZipFile();
     ZipOutputStream out = createZipOutputStream(f);
+
+    // Regular (non zip64) format.
     ZipEntry ze = new ZipEntry("x");
+    ze.setSize(0);
+    ze.setExtra(extra);
+    ze.setComment(comment);
+    out.putNextEntry(ze);
+    out.closeEntry();
+
+    // An entry without a length is assumed to be zip64.
+    ze = new ZipEntry("y");
     ze.setExtra(extra);
     ze.setComment(comment);
     out.putNextEntry(ze);
@@ -188,6 +219,9 @@
     try {
       assertEquals(comment, zipFile.getEntry("x").getComment());
       assertTrue(Arrays.equals(extra, zipFile.getEntry("x").getExtra()));
+
+      assertEquals(comment, zipFile.getEntry("y").getComment());
+      assertTrue(Arrays.equals(extra, zipFile.getEntry("y").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 a9ff56f..02210ac 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -11,497 +11,18 @@
  * 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.
+ * limitations under the License
  */
 
 package libcore.java.util.zip;
 
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-import java.util.zip.CRC32;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
-import junit.framework.TestCase;
 
-import tests.support.resource.Support_Resources;
+public final class ZipFileTest extends AbstractZipFileTest {
 
-public final class ZipFileTest extends TestCase {
-    /**
-     * Exercise Inflater's ability to refill the zlib's input buffer. As of this
-     * writing, this buffer's max size is 64KiB compressed bytes. We'll write a
-     * full megabyte of uncompressed data, which should be sufficient to exhaust
-     * the buffer. http://b/issue?id=2734751
-     */
-    public void testInflatingFilesRequiringZipRefill() throws IOException {
-        int originalSize = 1024 * 1024;
-        byte[] readBuffer = new byte[8192];
-        ZipFile zipFile = new ZipFile(createZipFile(1, originalSize));
-        for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
-            ZipEntry zipEntry = e.nextElement();
-            assertTrue("This test needs >64 KiB of compressed data to exercise Inflater",
-                    zipEntry.getCompressedSize() > (64 * 1024));
-            InputStream is = zipFile.getInputStream(zipEntry);
-            while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
-            is.close();
-        }
-        zipFile.close();
-    }
-
-    private static void replaceBytes(byte[] buffer, byte[] original, byte[] replacement) {
-        // Gotcha here: original and replacement must be the same length
-        assertEquals(original.length, replacement.length);
-        boolean found;
-        for(int i=0; i < buffer.length - original.length; i++) {
-            found = false;
-            if (buffer[i] == original[0]) {
-                found = true;
-                for (int j=0; j < original.length; j++) {
-                    if (buffer[i+j] != original[j]) {
-                        found = false;
-                        break;
-                    }
-                }
-            }
-            if (found) {
-                for (int j=0; j < original.length; j++) {
-                    buffer[i+j] = replacement[j];
-                }
-            }
-        }
-    }
-
-    private static void writeBytes(File f, byte[] bytes) throws IOException {
-        FileOutputStream out = new FileOutputStream(f);
-        out.write(bytes);
-        out.close();
-    }
-
-    /**
-     * Make sure we don't fail silently for duplicate entries.
-     * b/8219321
-     */
-    public void testDuplicateEntries() throws Exception {
-        String name1 = "test_file_name1";
-        String name2 = "test_file_name2";
-
-        // Create the good zip file.
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ZipOutputStream out = new ZipOutputStream(baos);
-        out.putNextEntry(new ZipEntry(name2));
-        out.closeEntry();
-        out.putNextEntry(new ZipEntry(name1));
-        out.closeEntry();
-        out.close();
-
-        // Rewrite one of the filenames.
-        byte[] buffer = baos.toByteArray();
-        replaceBytes(buffer, name2.getBytes(), name1.getBytes());
-
-        // Write the result to a file.
-        File badZip = createTemporaryZipFile();
-        writeBytes(badZip, buffer);
-
-        // Check that we refuse to load the modified file.
-        try {
-            ZipFile bad = new ZipFile(badZip);
-            fail();
-        } catch (ZipException expected) {
-        }
-    }
-
-    /**
-     * 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);
-        zip.close();
-    }
-
-    public void testInflatingStreamsRequiringZipRefill() throws IOException {
-        int originalSize = 1024 * 1024;
-        byte[] readBuffer = new byte[8192];
-        ZipInputStream in = new ZipInputStream(new FileInputStream(createZipFile(1, originalSize)));
-        while (in.getNextEntry() != null) {
-            while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
-        }
-        in.close();
-    }
-
-    public void testZipFileWithLotsOfEntries() throws IOException {
-        int expectedEntryCount = 64*1024 - 1;
-        File f = createZipFile(expectedEntryCount, 0);
-        ZipFile zipFile = new ZipFile(f);
-        int entryCount = 0;
-        for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
-            ZipEntry zipEntry = e.nextElement();
-            ++entryCount;
-        }
-        assertEquals(expectedEntryCount, entryCount);
-        zipFile.close();
-    }
-
-    // http://code.google.com/p/android/issues/detail?id=36187
-    public void testZipFileLargerThan2GiB() throws IOException {
-        if (false) { // TODO: this test requires too much time and too much disk space!
-            File f = createZipFile(1024, 3*1024*1024);
-            ZipFile zipFile = new ZipFile(f);
-            int entryCount = 0;
-            for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
-                ZipEntry zipEntry = e.nextElement();
-                ++entryCount;
-            }
-            assertEquals(1024, entryCount);
-            zipFile.close();
-        }
-    }
-
-    public void testZip64Support() throws IOException {
-        try {
-            createZipFile(64*1024, 0);
-            fail(); // Make this test more like testHugeZipFile when we have Zip64 support.
-        } catch (ZipException expected) {
-        }
-    }
-
-    /**
-     * Compresses the given number of files, each of the given size, into a .zip archive.
-     */
-    private File createZipFile(int entryCount, int entrySize) throws IOException {
-        File result = createTemporaryZipFile();
-
-        byte[] writeBuffer = new byte[8192];
-        Random random = new Random();
-
-        ZipOutputStream out = createZipOutputStream(result);
-        try {
-            for (int entry = 0; entry < entryCount; ++entry) {
-                ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
-                out.putNextEntry(ze);
-
-                for (int i = 0; i < entrySize; i += writeBuffer.length) {
-                    random.nextBytes(writeBuffer);
-                    int byteCount = Math.min(writeBuffer.length, entrySize - i);
-                    out.write(writeBuffer, 0, byteCount);
-                }
-
-                out.closeEntry();
-            }
-        } finally {
-            out.close();
-        }
-        return result;
-    }
-
-    private File createTemporaryZipFile() throws IOException {
-        File result = File.createTempFile("ZipFileTest", "zip");
-        result.deleteOnExit();
-        return result;
-    }
-
-    private ZipOutputStream createZipOutputStream(File f) throws IOException {
-        return new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
-    }
-
-    public void testSTORED() throws IOException {
-        ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
-        CRC32 crc = new CRC32();
-
-        // Missing CRC, size, and compressed size => failure.
-        try {
-            ZipEntry ze = new ZipEntry("a");
-            ze.setMethod(ZipEntry.STORED);
-            out.putNextEntry(ze);
-            fail();
-        } catch (ZipException expected) {
-        }
-
-        // Missing CRC and compressed size => failure.
-        try {
-            ZipEntry ze = new ZipEntry("a");
-            ze.setMethod(ZipEntry.STORED);
-            ze.setSize(0);
-            out.putNextEntry(ze);
-            fail();
-        } catch (ZipException expected) {
-        }
-
-        // Missing CRC and size => failure.
-        try {
-            ZipEntry ze = new ZipEntry("a");
-            ze.setMethod(ZipEntry.STORED);
-            ze.setSize(0);
-            ze.setCompressedSize(0);
-            out.putNextEntry(ze);
-            fail();
-        } catch (ZipException expected) {
-        }
-
-        // Missing size and compressed size => failure.
-        try {
-            ZipEntry ze = new ZipEntry("a");
-            ze.setMethod(ZipEntry.STORED);
-            ze.setCrc(crc.getValue());
-            out.putNextEntry(ze);
-            fail();
-        } catch (ZipException expected) {
-        }
-
-        // Missing size is copied from compressed size.
-        {
-            ZipEntry ze = new ZipEntry("okay1");
-            ze.setMethod(ZipEntry.STORED);
-            ze.setCrc(crc.getValue());
-
-            assertEquals(-1, ze.getSize());
-            assertEquals(-1, ze.getCompressedSize());
-
-            ze.setCompressedSize(0);
-
-            assertEquals(-1, ze.getSize());
-            assertEquals(0, ze.getCompressedSize());
-
-            out.putNextEntry(ze);
-
-            assertEquals(0, ze.getSize());
-            assertEquals(0, ze.getCompressedSize());
-        }
-
-        // Missing compressed size is copied from size.
-        {
-            ZipEntry ze = new ZipEntry("okay2");
-            ze.setMethod(ZipEntry.STORED);
-            ze.setCrc(crc.getValue());
-
-            assertEquals(-1, ze.getSize());
-            assertEquals(-1, ze.getCompressedSize());
-
-            ze.setSize(0);
-
-            assertEquals(0, ze.getSize());
-            assertEquals(-1, ze.getCompressedSize());
-
-            out.putNextEntry(ze);
-
-            assertEquals(0, ze.getSize());
-            assertEquals(0, ze.getCompressedSize());
-        }
-
-        // Mismatched size and compressed size => failure.
-        try {
-            ZipEntry ze = new ZipEntry("a");
-            ze.setMethod(ZipEntry.STORED);
-            ze.setCrc(crc.getValue());
-            ze.setCompressedSize(1);
-            ze.setSize(0);
-            out.putNextEntry(ze);
-            fail();
-        } catch (ZipException expected) {
-        }
-
-        // Everything present => success.
-        ZipEntry ze = new ZipEntry("okay");
-        ze.setMethod(ZipEntry.STORED);
-        ze.setCrc(crc.getValue());
-        ze.setSize(0);
-        ze.setCompressedSize(0);
-        out.putNextEntry(ze);
-
-        out.close();
-    }
-
-    private String makeString(int count, String ch) {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < count; ++i) {
-            sb.append(ch);
-        }
-        return sb.toString();
-    }
-
-    public void testComments() throws Exception {
-        String expectedFileComment = "1 \u0666 2";
-        String expectedEntryComment = "a \u0666 b";
-
-        File file = createTemporaryZipFile();
-        ZipOutputStream out = createZipOutputStream(file);
-
-        // Is file comment length checking done on bytes or characters? (Should be bytes.)
-        out.setComment(null);
-        out.setComment(makeString(0xffff, "a"));
-        try {
-            out.setComment(makeString(0xffff + 1, "a"));
-            fail();
-        } catch (IllegalArgumentException expected) {
-        }
-        try {
-            out.setComment(makeString(0xffff, "\u0666"));
-            fail();
-        } catch (IllegalArgumentException expected) {
-        }
-
-        ZipEntry ze = new ZipEntry("a");
-
-        // Is entry comment length checking done on bytes or characters? (Should be bytes.)
-        ze.setComment(null);
-        ze.setComment(makeString(0xffff, "a"));
-        try {
-            ze.setComment(makeString(0xffff + 1, "a"));
-            fail();
-        } catch (IllegalArgumentException expected) {
-        }
-        try {
-            ze.setComment(makeString(0xffff, "\u0666"));
-            fail();
-        } catch (IllegalArgumentException expected) {
-        }
-
-        ze.setComment(expectedEntryComment);
-        out.putNextEntry(ze);
-        out.closeEntry();
-
-        out.setComment(expectedFileComment);
-        out.close();
-
-        ZipFile zipFile = new ZipFile(file);
-        assertEquals(expectedFileComment, zipFile.getComment());
-        assertEquals(expectedEntryComment, zipFile.getEntry("a").getComment());
-        zipFile.close();
-    }
-
-    public void test_getComment_unset() throws Exception {
-        File file = createTemporaryZipFile();
-        ZipOutputStream out = createZipOutputStream(file);
-        ZipEntry ze = new ZipEntry("test entry");
-        ze.setComment("per-entry comment");
-        out.putNextEntry(ze);
-        out.close();
-
-        ZipFile zipFile = new ZipFile(file);
-        assertEquals(null, zipFile.getComment());
-    }
-
-    // https://code.google.com/p/android/issues/detail?id=58465
-    public void test_NUL_in_filename() throws Exception {
-        File file = createTemporaryZipFile();
-
-        // We allow creation of a ZipEntry whose name contains a NUL byte,
-        // mainly because it's not likely to happen by accident and it's useful for testing.
-        ZipOutputStream out = createZipOutputStream(file);
-        out.putNextEntry(new ZipEntry("hello"));
-        out.putNextEntry(new ZipEntry("hello\u0000"));
-        out.close();
-
-        // But you can't open a ZIP file containing such an entry, because we reject it
-        // when we find it in the central directory.
-        try {
-            ZipFile zipFile = new ZipFile(file);
-            fail();
-        } catch (ZipException expected) {
-        }
-    }
-
-    public void testCrc() throws IOException {
-        ZipEntry ze = new ZipEntry("test");
-        ze.setMethod(ZipEntry.STORED);
-        ze.setSize(4);
-
-        // setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits).
-        try {
-            ze.setCrc(-1);
-        } catch (IllegalArgumentException expected) {
-        }
-
-        // You can set the CRC32 to 0xffffffff if you're slightly more careful though...
-        ze.setCrc(0xffffffffL);
-        assertEquals(0xffffffffL, ze.getCrc());
-
-        // And it actually works, even though we use -1L to mean "no CRC set"...
-        ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
-        out.putNextEntry(ze);
-        out.write(-1);
-        out.write(-1);
-        out.write(-1);
-        out.write(-1);
-        out.closeEntry();
-        out.close();
-    }
-
-    /**
-     * RI does not allow reading of an empty zip using a {@link ZipFile}.
-     */
-    public void testConstructorFailsWhenReadingEmptyZipArchive() throws IOException {
-
-        File resources = Support_Resources.createTempFolder();
-        File emptyZip = Support_Resources.copyFile(
-                resources, "java/util/zip", "EmptyArchive.zip");
-
-        try {
-            // The following should fail with an exception but if it doesn't then we need to clean
-            // up the resource so we need a reference to it.
-            ZipFile zipFile = new ZipFile(emptyZip);
-
-            // Clean up the resource.
-            try {
-                zipFile.close();
-            } catch (Exception e) {
-                // Ignore
-            }
-            fail();
-        } catch (ZipException expected) {
-            // expected
-        }
+    @Override
+    protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
+        return new ZipOutputStream(wrapped);
     }
 }
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
index e69f010..15600de 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
@@ -21,7 +21,6 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Random;
 import java.util.zip.ZipEntry;
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index c89886c..494d15e 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -847,6 +847,24 @@
         }
     }
 
+    public void testCipher_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+                put("Cipher.FOO SupportedKeyClasses", "None");
+            }
+        };
+
+        Security.addProvider(mockProvider);
+        try {
+            Cipher c = Cipher.getInstance("FOO", mockProvider);
+            c.init(Cipher.ENCRYPT_MODE, new MockKey());
+            assertEquals(mockProvider, c.getProvider());
+        } finally {
+            Security.removeProvider(mockProvider.getName());
+        }
+    }
+
     public void testCipher_getInstance_SuppliedProviderNotRegistered_MultipartTransform_Success()
             throws Exception {
         Provider mockProvider = new MockProvider("MockProvider") {
@@ -1136,6 +1154,9 @@
 
         final AlgorithmParameterSpec decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c);
         int decryptMode = getDecryptMode(algorithm);
+
+        test_Cipher_init_Decrypt_NullParameters(c, decryptMode, encryptKey, decryptSpec != null);
+
         c.init(decryptMode, encryptKey, decryptSpec);
         assertEquals(cipherID + " getBlockSize() decryptMode",
                      getExpectedBlockSize(algorithm, decryptMode, providerName), c.getBlockSize());
@@ -1268,6 +1289,53 @@
         }
     }
 
+    private void test_Cipher_init_Decrypt_NullParameters(Cipher c, int decryptMode, Key encryptKey,
+            boolean needsParameters) throws Exception {
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null, (SecureRandom) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameters) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameters) null, (SecureRandom) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+    }
+
     public void testInputPKCS1Padding() throws Exception {
         for (String provider : RSA_PROVIDERS) {
             testInputPKCS1Padding(provider);
@@ -2797,6 +2865,17 @@
         }
     }
 
+    public void testCipher_Update_WithZeroLengthInput_ReturnsNull() throws Exception {
+        SecretKey key = new SecretKeySpec(AES_128_KEY, "AES");
+        Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
+        c.init(Cipher.ENCRYPT_MODE, key);
+        assertNull(c.update(new byte[0]));
+        assertNull(c.update(new byte[c.getBlockSize() * 2], 0, 0));
+
+        // Try with non-zero offset just in case the implementation mixes up offset and inputLen
+        assertNull(c.update(new byte[c.getBlockSize() * 2], 16, 0));
+    }
+
     private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) throws Exception {
         SecretKey key = new SecretKeySpec(p.key, "AES");
         Cipher c = Cipher.getInstance(
diff --git a/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
new file mode 100644
index 0000000..ba03f75
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.javax.crypto;
+
+import java.security.Provider;
+import java.security.Security;
+
+import javax.crypto.KeyAgreement;
+
+import junit.framework.TestCase;
+
+public class KeyAgreementTest extends TestCase {
+    private static abstract class MockProvider extends Provider {
+        public MockProvider(String name) {
+            super(name, 1.0, "Mock provider used for testing");
+            setup();
+        }
+
+        public abstract void setup();
+    }
+
+    public void testKeyAgreement_getInstance_SuppliedProviderNotRegistered_Success()
+            throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+            }
+        };
+
+        {
+            KeyAgreement c = KeyAgreement.getInstance("FOO", mockProvider);
+            c.init(new MockKey());
+            assertEquals(mockProvider, c.getProvider());
+        }
+    }
+
+    public void testKeyAgreement_getInstance_DoesNotSupportKeyClass_Success()
+            throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+                put("KeyAgreement.FOO SupportedKeyClasses", "none");
+
+            }
+        };
+
+        Security.addProvider(mockProvider);
+        try {
+            KeyAgreement c = KeyAgreement.getInstance("FOO", mockProvider);
+            c.init(new MockKey());
+            assertEquals(mockProvider, c.getProvider());
+        } finally {
+            Security.removeProvider(mockProvider.getName());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java b/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
index 8bbd548..5763562 100644
--- a/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
@@ -44,6 +44,15 @@
                 if (!type.equals("KeyGenerator")) {
                     continue;
                 }
+
+                // Do not test AndroidKeyStore's KeyGenerator. It cannot be initialized without
+                // providing AndroidKeyStore-specific algorithm parameters.
+                // It's OKish not to test AndroidKeyStore's KeyGenerator here because it's tested
+                // by cts/tests/test/keystore.
+                if ("AndroidKeyStore".equals(provider.getName())) {
+                    continue;
+                }
+
                 String algorithm = service.getAlgorithm();
                 try {
                     // KeyGenerator.getInstance(String)
diff --git a/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java b/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java
new file mode 100644
index 0000000..574dbeb
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.javax.crypto;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+
+/**
+ * Mock KeyAgreementSpi used by {@link KeyAgreementTest}.
+ */
+public class MockKeyAgreementSpi extends KeyAgreementSpi {
+    public static class SpecificKeyTypes extends MockKeyAgreementSpi {
+        @Override
+        public void checkKeyType(Key key) throws InvalidKeyException {
+            if (!(key instanceof MockKey)) {
+                throw new InvalidKeyException("Must be MockKey!");
+            }
+        }
+    }
+
+    public static class SpecificKeyTypes2 extends MockKeyAgreementSpi {
+        @Override
+        public void checkKeyType(Key key) throws InvalidKeyException {
+            System.err.println("Checking key of type " + key.getClass().getName());
+            if (!(key instanceof MockKey2)) {
+                throw new InvalidKeyException("Must be MockKey2!");
+            }
+        }
+    }
+
+    public static class AllKeyTypes extends MockKeyAgreementSpi {
+    }
+
+    public void checkKeyType(Key key) throws InvalidKeyException {
+    }
+
+    @Override
+    protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException,
+            IllegalStateException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected byte[] engineGenerateSecret() throws IllegalStateException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+            throws IllegalStateException, ShortBufferException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException,
+            NoSuchAlgorithmException, InvalidKeyException {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+        checkKeyType(key);
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        checkKeyType(key);
+    }
+}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
index feecebe..07ecd12 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
@@ -22,6 +22,7 @@
 import java.nio.charset.StandardCharsets;
 import java.security.Principal;
 import java.security.PublicKey;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -30,21 +31,32 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import javax.net.ssl.DefaultHostnameVerifier;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
 import javax.security.auth.x500.X500Principal;
 import junit.framework.TestCase;
 
+/**
+ * Tests for the platform-default {@link HostnameVerifier} as provided by
+ * {@link HttpsURLConnection#getDefaultHostnameVerifier()}.
+ */
 public final class DefaultHostnameVerifierTest extends TestCase {
     private static final int ALT_UNKNOWN = 0;
     private static final int ALT_DNS_NAME = 2;
     private static final int ALT_IPA_NAME = 7;
 
-    private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier();
+    private final HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
 
     public void testVerify() {
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")));
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
+        assertTrue(verifyWithServerCertificate(
+                "imap.g.com", new StubX509Certificate("cn=imap.g.com")));
+        assertFalse(verifyWithServerCertificate(
+                "imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
+        assertFalse(verifyWithServerCertificate(
+                "imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
     }
 
     /**
@@ -52,32 +64,33 @@
      * be used as the identity and the CN should be ignored.
      */
     public void testSubjectAltNameAndCn() {
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
-        assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")
-                .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertFalse(
+                verifyWithServerCertificate("imap.g.com", new StubX509Certificate("cn=imap.g.com")
+                        .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
+        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")));
     }
 
     public void testSubjectAltNameWithWildcard() {
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")));
     }
 
     public void testSubjectAltNameWithIpAddress() {
-        assertTrue(verifier.verify("1.2.3.4", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
-        assertFalse(verifier.verify("1.2.3.5", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("1.2.3.5", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
-        assertTrue(verifier.verify("192.168.100.1", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("192.168.100.1", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")));
     }
 
     public void testUnknownSubjectAltName() {
         // Has unknown subject alternative names
-        assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -85,7 +98,7 @@
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
-        assertTrue(verifier.verify("2.33.44.55", new StubX509Certificate("")
+        assertTrue(verifyWithServerCertificate("2.33.44.55", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -93,7 +106,7 @@
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
-        assertFalse(verifier.verify("g.com", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("g.com", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -101,7 +114,7 @@
                 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
-        assertFalse(verifier.verify("2.33.44.1", new StubX509Certificate("")
+        assertFalse(verifyWithServerCertificate("2.33.44.1", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
                 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -111,40 +124,129 @@
                 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
     }
 
-    public void testWildcardMatchesWildcardSuffix() {
-        assertTrue(verifier.verifyHostName("b.c.d", "*.b.c.d"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "*.imap.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com.au", "*.imap.google.com"));
+    public void testWildcardsRejectedForIpAddress() {
+        assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")));
+        assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "*.2.3.4")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*.2.3.4")));
+        assertFalse(verifyWithServerCertificate(
+                "2001:1234::1", new StubX509Certificate("cn=*:1234::1")));
+        assertFalse(verifyWithServerCertificate(
+                "2001:1234::1", new StubX509Certificate("cn=*:1234::1")
+                .addSubjectAlternativeName(ALT_IPA_NAME, "*:1234::1")
+                .addSubjectAlternativeName(ALT_DNS_NAME, "*:1234::1")));
     }
 
-    public void testWildcardMatchingSubstring() {
-        assertTrue(verifier.verifyHostName("b.c.d", "b*.c.d"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "ima*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com.au", "ima*.google.com"));
+    public void testNullParameters() {
+        // Confirm that neither of the parameters used later in the test cause the verifier to blow
+        // up
+        String hostname = "www.example.com";
+        StubSSLSession session = new StubSSLSession();
+        session.peerCertificates =
+                new Certificate[] {new StubX509Certificate("cn=www.example.com")};
+        verifier.verify(hostname, session);
+
+        try {
+            verifier.verify(hostname, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            verifier.verify(null, session);
+            fail();
+        } catch (NullPointerException expected) {
+        }
     }
 
-    public void testWildcardMatchingEmptySubstring() {
-        assertTrue(verifier.verifyHostName("imap.google.com", "imap*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com.au", "imap*.google.com"));
+    public void testInvalidDomainNames() {
+        assertFalse(verifyWithDomainNamePattern("", ""));
+        assertFalse(verifyWithDomainNamePattern(".test.example.com", ".test.example.com"));
+        assertFalse(verifyWithDomainNamePattern("ex*ample.com", "ex*ample.com"));
+        assertFalse(verifyWithDomainNamePattern("example.com..", "example.com."));
+        assertFalse(verifyWithDomainNamePattern("example.com.", "example.com.."));
     }
 
-    public void testWildcardMatchesChildDomain() {
-        assertFalse(verifier.verifyHostName("a.b.c.d", "*.c.d"));
+    public void testWildcardCharacterMustBeLeftMostLabelOnly() {
+        assertFalse(verifyWithDomainNamePattern("test.www.example.com", "test.*.example.com"));
+        assertFalse(verifyWithDomainNamePattern("www.example.com", "www.*.com"));
+        assertFalse(verifyWithDomainNamePattern("www.example.com", "www.example.*"));
+        assertFalse(verifyWithDomainNamePattern("www.example.com", "*www.example.com"));
+        assertFalse(verifyWithDomainNamePattern("www.example.com", "*w.example.com"));
+        assertFalse(verifyWithDomainNamePattern("www.example.com", "w*w.example.com"));
+        assertFalse(verifyWithDomainNamePattern("www.example.com", "w*.example.com"));
+        assertFalse(verifyWithDomainNamePattern("www.example.com", "www*.example.com"));
+    }
+
+    public void testWildcardCannotMatchEmptyLabel() {
+        assertFalse(verifyWithDomainNamePattern("example.com", "*.example.com"));
+        assertFalse(verifyWithDomainNamePattern(".example.com", "*.example.com"));
+    }
+
+    public void testWildcardCannotMatchChildDomain() {
+        assertFalse(verifyWithDomainNamePattern("sub.www.example.com", "*.example.com"));
+    }
+
+    public void testWildcardRejectedForSingleLabelPatterns() {
+        assertFalse(verifyWithDomainNamePattern("d", "*"));
+        assertFalse(verifyWithDomainNamePattern("d.", "*."));
+        assertFalse(verifyWithDomainNamePattern("d", "d*"));
+        assertFalse(verifyWithDomainNamePattern("d.", "d*."));
+        assertFalse(verifyWithDomainNamePattern("d", "*d"));
+        assertFalse(verifyWithDomainNamePattern("d.", "*d."));
+        assertFalse(verifyWithDomainNamePattern("ddd", "d*d"));
+        assertFalse(verifyWithDomainNamePattern("ddd.", "d*d."));
+    }
+
+    public void testNoPrefixMatch() {
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "imap.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "*.google.com"));
     }
 
     public void testVerifyHostName() {
-        assertTrue(verifier.verifyHostName("a.b.c.d", "a.b.c.d"));
-        assertTrue(verifier.verifyHostName("a.b.c.d", "*.b.c.d"));
-        assertFalse(verifier.verifyHostName("a.b.c.d", "*.*.c.d"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "imap.google.com"));
-        assertFalse(verifier.verifyHostName("imap2.google.com", "imap.google.com"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "*.google.com"));
-        assertTrue(verifier.verifyHostName("imap2.google.com", "*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com", "*.googl.com"));
-        assertFalse(verifier.verifyHostName("imap2.google2.com", "*.google3.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com", "a*.google.com"));
-        assertFalse(verifier.verifyHostName("imap.google.com", "ix*.google.com"));
-        assertTrue(verifier.verifyHostName("imap.google.com", "iMap.Google.Com"));
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d", "a.b.c.d"));
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d", "*.b.c.d"));
+        assertFalse(verifyWithDomainNamePattern("a.b.c.d", "*.*.c.d"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "imap.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google.com", "imap.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap2.google.com", "*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com", "*.googl.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google2.com", "*.google3.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com", "a*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com", "ix*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com", "iMap.Google.Com"));
+        assertTrue(verifyWithDomainNamePattern("weird", "weird"));
+        assertTrue(verifyWithDomainNamePattern("weird", "weird."));
+
+        // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
+        assertFalse(verifyWithDomainNamePattern("weird", "weird*"));
+        assertFalse(verifyWithDomainNamePattern("weird", "*weird"));
+        assertFalse(verifyWithDomainNamePattern("weird", "weird*."));
+        assertFalse(verifyWithDomainNamePattern("weird", "weird.*"));
+    }
+
+    public void testVerifyAbsoluteHostName() {
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "a.b.c.d"));
+        assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "*.b.c.d"));
+        assertFalse(verifyWithDomainNamePattern("a.b.c.d.", "*.*.c.d"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "imap.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google.com.", "imap.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap2.google.com.", "*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "*.googl.com"));
+        assertFalse(verifyWithDomainNamePattern("imap2.google2.com.", "*.google3.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "a*.google.com"));
+        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "ix*.google.com"));
+        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "iMap.Google.Com"));
+        assertTrue(verifyWithDomainNamePattern("weird.", "weird"));
+        assertTrue(verifyWithDomainNamePattern("weird.", "weird."));
+
+        // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
+        assertFalse(verifyWithDomainNamePattern("weird.", "*weird"));
+        assertFalse(verifyWithDomainNamePattern("weird.", "weird*"));
+        assertFalse(verifyWithDomainNamePattern("weird.", "weird*."));
+        assertFalse(verifyWithDomainNamePattern("weird.", "weird.*"));
     }
 
     public void testSubjectOnlyCert() throws Exception {
@@ -168,8 +270,8 @@
                 + "rs2oQLwOLnuifH52ey9+tJguabo+brlYYigAuWWFEzJfBzikDkIwnE/L7wlrypIk\n"
                 + "taXDWI4=\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("www.example.com", cert));
-        assertFalse(verifier.verify("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
     }
 
     public void testSubjectAltOnlyCert() throws Exception {
@@ -192,8 +294,8 @@
                 + "JPRynf9244Pn0Sr/wsnmdsTRFIFYynrc51hQ7DkwbUxpcaewkZzilru/SwZ3+pPT\n"
                 + "9JSqm5hJ1pg5WDlPkW7c/1VA0/141N52Q8MIU+2ZpuOj\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("www.example.com", cert));
-        assertFalse(verifier.verify("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
     }
 
     public void testSubjectWithAltNamesCert() throws Exception {
@@ -219,10 +321,10 @@
                 + "hrTVypLSoRXuTB2aWilu4p6aNh84xTdyqo2avtNr2MiQMZIcdamBq8LdBIAShFXI\n"
                 + "h5G2eVGXH/Y=\n"
                 + "-----END CERTIFICATE-----");
-        assertFalse(verifier.verify("www.example.com", cert));
-        assertTrue(verifier.verify("www2.example.com", cert));
-        assertTrue(verifier.verify("www3.example.com", cert));
-        assertFalse(verifier.verify("www4.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www3.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www4.example.com", cert));
     }
 
     public void testSubjectWithWildAltNamesCert() throws Exception {
@@ -247,11 +349,11 @@
                 + "Y3R0HZvKzNIU3pwAm69HCJoG+/9MZEIDJb0WJc5UygxDT45XE9zQMQe4dBOTaNXT\n"
                 + "+ntgaB62kE10HzrzpqXAgoAWxWK4RzFcUpBWw9qYq9xOCewJ\n"
                 + "-----END CERTIFICATE-----");
-        assertFalse(verifier.verify("www.example.com", cert));
-        assertFalse(verifier.verify("www2.example.com", cert));
-        assertTrue(verifier.verify("www.example2.com", cert));
-        assertTrue(verifier.verify("abc.example2.com", cert));
-        assertFalse(verifier.verify("www.example3.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example2.com", cert));
+        assertTrue(verifyWithServerCertificate("abc.example2.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example3.com", cert));
     }
 
     public void testWildAltNameOnlyCert() throws Exception {
@@ -274,9 +376,9 @@
                 + "va++ow5r1VxQXFJc0ZPzsDo+6TlktoDHaRQJGMqQomqHWT4i7F5UZgf6BHGfEUPU\n"
                 + "qep+GsF3QRHSBtpObWkVDZNFvky3a1iZ2q25+hFIqQ==\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("www.example.com", cert));
-        assertTrue(verifier.verify("www2.example.com", cert));
-        assertFalse(verifier.verify("www.example2.com", cert));
+        assertTrue(verifyWithServerCertificate("www.example.com", cert));
+        assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+        assertFalse(verifyWithServerCertificate("www.example2.com", cert));
     }
 
     public void testAltIpOnlyCert() throws Exception {
@@ -299,8 +401,48 @@
                 + "WPjHQcWfpkFzAF5wyOq0kveVfx0g5xPhOVDd+U+q7WastbXICpCoHp9FxISmZVik\n"
                 + "sAyifp8agkYdzaSh55fFmKXlFnRsQw==\n"
                 + "-----END CERTIFICATE-----");
-        assertTrue(verifier.verify("192.168.10.1", cert));
-        assertFalse(verifier.verify("192.168.10.2", cert));
+        assertTrue(verifyWithServerCertificate("192.168.10.1", cert));
+        assertFalse(verifyWithServerCertificate("192.168.10.2", cert));
+    }
+
+    /**
+     * Verifies the provided hostname against the provided domain name pattern from server
+     * certificate.
+     */
+    private boolean verifyWithDomainNamePattern(String hostname, String pattern) {
+        StubSSLSession session = new StubSSLSession();
+
+        // Verify using a certificate where the pattern is in the CN
+        session.peerCertificates = new Certificate[] {
+                new StubX509Certificate("cn=\"" + pattern + "\"")
+        };
+        boolean resultWhenPatternInCn = verifier.verify(hostname, session);
+
+        // Verify using a certificate where the pattern is in a DNS SubjectAltName
+        session.peerCertificates = new Certificate[] {
+                new StubX509Certificate("ou=test")
+                        .addSubjectAlternativeName(ALT_DNS_NAME, pattern)
+        };
+        boolean resultWhenPatternInSubjectAltName = verifier.verify(hostname, session);
+
+        // Assert that in both cases the verifier gives the same result
+        if (resultWhenPatternInCn != resultWhenPatternInSubjectAltName) {
+            fail("Different results between pattern in CN and SubjectAltName."
+                    + " hostname : " + hostname + ", pattern: " + pattern
+                    + ", when pattern in CN: " + resultWhenPatternInCn
+                    + ", when pattern in SubjectAltName: " + resultWhenPatternInSubjectAltName);
+        }
+        return resultWhenPatternInCn;
+    }
+
+    /**
+     * Verifies the provided hostname against the provided server certificate.
+     */
+    private boolean verifyWithServerCertificate(String hostname, X509Certificate certificate) {
+        StubSSLSession session = new StubSSLSession();
+        session.peerCertificates =
+                (certificate != null) ? new Certificate[] {certificate} : new Certificate[0];
+        return verifier.verify(hostname, session);
     }
 
     X509Certificate parseCertificate(String encoded) throws Exception {
@@ -308,6 +450,117 @@
         return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(in);
     }
 
+    private static class StubSSLSession implements SSLSession {
+
+        public Certificate[] peerCertificates = new Certificate[0];
+
+        @Override
+        public int getApplicationBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getCipherSuite() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getCreationTime() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public byte[] getId() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getLastAccessedTime() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Certificate[] getLocalCertificates() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Principal getLocalPrincipal() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getPacketBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+                throws SSLPeerUnverifiedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+            return peerCertificates;
+        }
+
+        @Override
+        public String getPeerHost() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getPeerPort() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getProtocol() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public SSLSessionContext getSessionContext() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Object getValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getValueNames() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void invalidate() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        public void putValue(String name, Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void removeValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
     private static class StubX509Certificate extends X509Certificate {
         private final X500Principal subjectX500Principal;
         private Collection<List<?>> subjectAlternativeNames;
diff --git a/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java b/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java
new file mode 100644
index 0000000..cbaea20
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.javax.net.ssl;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+
+public class HttpsURLConnectionTest extends TestCase {
+
+  /**
+   * HTTPS URL which cannot be resolved and is thus safe to use in tests where network traffic
+   * should be avoided.
+   */
+  private static final String UNRESOLVABLE_HTTPS_URL = "https:///";
+
+  public void testDefaultHostnameVerifierNotNull() {
+    assertNotNull(HttpsURLConnection.getDefaultHostnameVerifier());
+  }
+
+  public void testDefaultHostnameVerifierUsedForNewConnectionsByDefault() throws IOException {
+    HostnameVerifier originalHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+    HttpsURLConnection connection =
+        (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+    try {
+      assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+    } finally {
+      connection.disconnect();
+    }
+
+    HostnameVerifier anotherVerifier = new FakeHostnameVerifier();
+    try {
+      HttpsURLConnection.setDefaultHostnameVerifier(anotherVerifier);
+      connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+      try {
+        assertSame(anotherVerifier, connection.getHostnameVerifier());
+      } finally {
+        connection.disconnect();
+      }
+
+      HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+      connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+      try {
+        assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+      } finally {
+        connection.disconnect();
+      }
+    } finally {
+      HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+    }
+  }
+
+  public void testDefaultSSLSocketFactoryNotNull() {
+    assertNotNull(HttpsURLConnection.getDefaultSSLSocketFactory());
+  }
+
+  public void testDefaultSSLSocketFactoryUsedForNewConnectionsByDefault() throws IOException {
+    SSLSocketFactory originalFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+    HttpsURLConnection connection =
+        (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+    try {
+      assertSame(originalFactory, connection.getSSLSocketFactory());
+    } finally {
+      connection.disconnect();
+    }
+
+    SSLSocketFactory anotherFactory = new SSLSocketFactoryTest.FakeSSLSocketFactory();
+    try {
+      HttpsURLConnection.setDefaultSSLSocketFactory(anotherFactory);
+      connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+      try {
+        assertSame(anotherFactory, connection.getSSLSocketFactory());
+      } finally {
+        connection.disconnect();
+      }
+
+      HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+      connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+      try {
+        assertSame(originalFactory, connection.getSSLSocketFactory());
+      } finally {
+        connection.disconnect();
+      }
+    } finally {
+      HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+    }
+  }
+
+  private static class FakeHostnameVerifier implements HostnameVerifier {
+    @Override
+    public boolean verify(String hostname, SSLSession session) {
+      return true;
+    }
+  }
+}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
index dccadbd..533849c 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
@@ -26,7 +26,6 @@
 import java.security.UnrecoverableKeyException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Callable;
 import libcore.io.IoUtils;
@@ -82,14 +81,14 @@
     }
 
     public void test_SSLContext_defaultConfiguration() throws Exception {
-        SSLDefaultConfigurationAsserts.assertSSLContext(SSLContext.getDefault());
+        SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(SSLContext.getDefault());
 
         for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
             SSLContext sslContext = SSLContext.getInstance(protocol);
             if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
                 sslContext.init(null, null, null);
             }
-            SSLDefaultConfigurationAsserts.assertSSLContext(sslContext);
+            SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(sslContext);
         }
     }
 
@@ -149,6 +148,27 @@
                 sslContext);
     }
 
+    public void test_SSLContext_init_correctProtocolVersionsEnabled() throws Exception {
+        for (String tlsVersion : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            // Don't test the "Default" instance.
+            if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(tlsVersion)) {
+                continue;
+            }
+
+            SSLContext context = SSLContext.getInstance(tlsVersion);
+            context.init(null, null, null);
+
+            StandardNames.assertSSLContextEnabledProtocols(tlsVersion, ((SSLSocket) (context.getSocketFactory()
+                    .createSocket())).getEnabledProtocols());
+            StandardNames.assertSSLContextEnabledProtocols(tlsVersion, ((SSLServerSocket) (context
+                    .getServerSocketFactory().createServerSocket())).getEnabledProtocols());
+            StandardNames.assertSSLContextEnabledProtocols(tlsVersion, context.getDefaultSSLParameters()
+                    .getProtocols());
+            StandardNames.assertSSLContextEnabledProtocols(tlsVersion, context.createSSLEngine()
+                    .getEnabledProtocols());
+        }
+    }
+
     private static void assertEnabledCipherSuites(
             List<String> expectedCipherSuites, SSLContext sslContext) throws Exception {
         assertContentsInOrder(
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index df4585f..5e3a3d5 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -65,7 +65,7 @@
     }
 
     public void test_SSLEngine_defaultConfiguration() throws Exception {
-        SSLDefaultConfigurationAsserts.assertSSLEngine(
+        SSLConfigurationAsserts.assertSSLEngineDefaultConfiguration(
                 TestSSLContext.create().clientContext.createSSLEngine());
     }
 
@@ -203,24 +203,31 @@
                        : new String[] { cipherSuite });
 
             // Check that handshake succeeds.
-            TestSSLEnginePair pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
-                @Override
-                void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
-                    client.setEnabledCipherSuites(cipherSuiteArray);
-                    server.setEnabledCipherSuites(cipherSuiteArray);
+            TestSSLEnginePair pair = null;
+            try {
+                pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+                    @Override
+                    void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                        client.setEnabledCipherSuites(cipherSuiteArray);
+                        server.setEnabledCipherSuites(cipherSuiteArray);
+                    }
+                });
+                assertConnected(pair);
+
+                boolean needsRecordSplit =
+                        ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
+                                || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
+                                && cipherSuite.contains("_CBC_");
+
+                assertSendsCorrectly("This is the client. Hello!".getBytes(),
+                        pair.client, pair.server, needsRecordSplit);
+                assertSendsCorrectly("This is the server. Hi!".getBytes(),
+                        pair.server, pair.client, needsRecordSplit);
+            } finally {
+                if (pair != null) {
+                    pair.close();
                 }
-            });
-            assertConnected(pair);
-
-            boolean needsRecordSplit =
-                    ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
-                            || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
-                    && cipherSuite.contains("_CBC_");
-
-            assertSendsCorrectly("This is the client. Hello!".getBytes(),
-                    pair.client, pair.server, needsRecordSplit);
-            assertSendsCorrectly("This is the server. Hi!".getBytes(),
-                    pair.server, pair.client, needsRecordSplit);
+            }
 
             // Check that handshake fails when the server does not possess the private key
             // corresponding to the server's certificate. This is achieved by using SSLContext
@@ -234,17 +241,23 @@
                 serverAuthenticatedUsingPublicKey = false;
             }
             if (serverAuthenticatedUsingPublicKey) {
+                TestSSLEnginePair p = null;
                 try {
-                    TestSSLEnginePair p = TestSSLEnginePair.create(
+                    p = TestSSLEnginePair.create(
                             cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
-                        @Override
+                                @Override
                                 void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
-                            client.setEnabledCipherSuites(cipherSuiteArray);
-                            server.setEnabledCipherSuites(cipherSuiteArray);
-                        }
-                    });
+                                    client.setEnabledCipherSuites(cipherSuiteArray);
+                                    server.setEnabledCipherSuites(cipherSuiteArray);
+                                }
+                            });
                     assertNotConnected(p);
-                } catch (IOException expected) {}
+                } catch (IOException expected) {
+                } finally {
+                    if (p != null) {
+                        p.close();
+                    }
+                }
             }
             } catch (Exception e) {
                 String message = ("Problem trying to connect cipher suite " + cipherSuite);
@@ -432,21 +445,28 @@
             fail();
         } catch (IllegalStateException expected) {
         }
-
-        assertConnected(TestSSLEnginePair.create(null));
-
         c.close();
+
+        TestSSLEnginePair p = TestSSLEnginePair.create(null);
+        assertConnected(p);
+        p.close();
+
     }
 
     public void test_SSLEngine_beginHandshake_noKeyStore() throws Exception {
         TestSSLContext c = TestSSLContext.create(null, null, null, null, null, null, null, null,
                                                  SSLContext.getDefault(), SSLContext.getDefault());
+        SSLEngine[] p = null;
         try {
             // TODO Fix KnownFailure AlertException "NO SERVER CERTIFICATE FOUND"
             // ServerHandshakeImpl.selectSuite should not select a suite without a required cert
-            TestSSLEnginePair.connect(c, null);
+            p = TestSSLEnginePair.connect(c, null);
             fail();
         } catch (SSLHandshakeException expected) {
+        } finally {
+            if (p != null) {
+                TestSSLEnginePair.close(p);
+            }
         }
         c.close();
     }
@@ -456,6 +476,7 @@
         SSLEngine[] engines = TestSSLEnginePair.connect(c, null);
         assertConnected(engines[0], engines[1]);
         c.close();
+        TestSSLEnginePair.close(engines);
     }
 
     public void test_SSLEngine_getUseClientMode() throws Exception {
@@ -467,33 +488,47 @@
 
     public void test_SSLEngine_setUseClientMode() throws Exception {
         boolean[] finished;
+        TestSSLEnginePair p = null;
 
         // client is client, server is server
         finished = new boolean[2];
-        assertConnected(test_SSLEngine_setUseClientMode(true, false, finished));
+        p = test_SSLEngine_setUseClientMode(true, false, finished);
+        assertConnected(p);
         assertTrue(finished[0]);
         assertTrue(finished[1]);
+        p.close();
 
         // client is server, server is client
         finished = new boolean[2];
-        assertConnected(test_SSLEngine_setUseClientMode(false, true, finished));
+        p = test_SSLEngine_setUseClientMode(false, true, finished);
+        assertConnected(p);
         assertTrue(finished[0]);
         assertTrue(finished[1]);
+        p.close();
 
         // both are client
         /*
          * Our implementation throws an SSLHandshakeException, but RI just
          * stalls forever
          */
+        p = null;
         try {
-            assertNotConnected(test_SSLEngine_setUseClientMode(true, true, null));
+            p = test_SSLEngine_setUseClientMode(true, true, null);
+            assertNotConnected(p);
             assertTrue(StandardNames.IS_RI);
         } catch (SSLHandshakeException maybeExpected) {
             assertFalse(StandardNames.IS_RI);
+        } finally {
+            if (p != null) {
+                p.close();
+            }
+
         }
 
+        p = test_SSLEngine_setUseClientMode(false, false, null);
         // both are server
-        assertNotConnected(test_SSLEngine_setUseClientMode(false, false, null));
+        assertNotConnected(p);
+        p.close();
     }
 
     public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception {
@@ -510,6 +545,7 @@
             fail();
         } catch (IllegalArgumentException expected) {
         }
+        pair.close();
     }
 
     private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode,
@@ -572,6 +608,7 @@
                                                     p.client.getSession().getLocalCertificates());
         clientAuthContext.close();
         c.close();
+        p.close();
     }
 
    /**
@@ -591,6 +628,7 @@
         });
         assertConnected(p);
         clientAuthContext.close();
+        p.close();
     }
 
    /**
@@ -604,8 +642,9 @@
         TestSSLContext clientAuthContext
                 = TestSSLContext.create(TestKeyStore.getClient(),
                                         TestKeyStore.getServer());
+        TestSSLEnginePair p = null;
         try {
-            TestSSLEnginePair.create(clientAuthContext,
+            p = TestSSLEnginePair.create(clientAuthContext,
                              new TestSSLEnginePair.Hooks() {
                 @Override
                 void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
@@ -616,6 +655,9 @@
         } catch (SSLHandshakeException expected) {
         } finally {
             clientAuthContext.close();
+            if (p != null) {
+                p.close();
+            }
         }
     }
 
@@ -624,11 +666,13 @@
         SSLEngine e = c.clientContext.createSSLEngine();
         assertTrue(e.getEnableSessionCreation());
         c.close();
+        TestSSLEnginePair.close(new SSLEngine[] { e });
     }
 
     public void test_SSLEngine_setEnableSessionCreation_server() throws Exception {
+        TestSSLEnginePair p = null;
         try {
-            TestSSLEnginePair p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+            p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
                 @Override
                 void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
                     server.setEnableSessionCreation(false);
@@ -639,12 +683,17 @@
             assertNotConnected(p);
         } catch (SSLException maybeExpected) {
             assertFalse(StandardNames.IS_RI);
+        } finally {
+            if (p != null) {
+                p.close();
+            }
         }
     }
 
     public void test_SSLEngine_setEnableSessionCreation_client() throws Exception {
+        TestSSLEnginePair p = null;
         try {
-            TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+            p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
                 @Override
                 void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
                     client.setEnableSessionCreation(false);
@@ -652,6 +701,10 @@
             });
             fail();
         } catch (SSLException expected) {
+        } finally {
+            if (p != null) {
+                p.close();
+            }
         }
     }
 
@@ -735,5 +788,6 @@
         assertNotNull(test.server);
         assertNotNull(test.client);
         assertConnected(test);
+        test.close();
     }
 }
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
index ea9c3f0..cda1fb8 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
@@ -22,7 +22,7 @@
 public class SSLServerSocketFactoryTest extends TestCase {
 
   public void testDefaultConfiguration() throws Exception {
-    SSLDefaultConfigurationAsserts.assertSSLServerSocketFactory(
+    SSLConfigurationAsserts.assertSSLServerSocketFactoryDefaultConfiguration(
         (SSLServerSocketFactory) SSLServerSocketFactory.getDefault());
   }
 }
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
index 59c44c1..d2c0f48 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
@@ -24,7 +24,7 @@
 public class SSLServerSocketTest extends TestCase {
 
   public void testDefaultConfiguration() throws Exception {
-    SSLDefaultConfigurationAsserts.assertSSLServerSocket(
+    SSLConfigurationAsserts.assertSSLServerSocketDefaultConfiguration(
         (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket());
   }
 
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
index acf69c0..83b690b 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
@@ -210,7 +210,7 @@
     }
 
     public void test_SSLSocketFactory_defaultConfiguration() throws Exception {
-        SSLDefaultConfigurationAsserts.assertSSLSocketFactory(
+        SSLConfigurationAsserts.assertSSLSocketFactoryDefaultConfiguration(
                 (SSLSocketFactory) SSLSocketFactory.getDefault());
     }
 
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 4af7f5a..8e4519d 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -16,6 +16,8 @@
 
 package libcore.javax.net.ssl;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -26,15 +28,18 @@
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.SocketTimeoutException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -66,11 +71,23 @@
 import libcore.io.Streams;
 import libcore.java.security.StandardNames;
 import libcore.java.security.TestKeyStore;
+import libcore.tlswire.handshake.CipherSuite;
+import libcore.tlswire.handshake.ClientHello;
+import libcore.tlswire.handshake.CompressionMethod;
+import libcore.tlswire.handshake.HandshakeMessage;
+import libcore.tlswire.handshake.HelloExtension;
+import libcore.tlswire.handshake.ServerNameHelloExtension;
+import libcore.tlswire.record.TlsProtocols;
+import libcore.tlswire.record.TlsRecord;
+import libcore.tlswire.util.TlsProtocolVersion;
+import tests.util.ForEachRunner;
+import tests.util.DelegatingSSLSocketFactory;
+import tests.util.Pair;
 
 public class SSLSocketTest extends TestCase {
 
     public void test_SSLSocket_defaultConfiguration() throws Exception {
-        SSLDefaultConfigurationAsserts.assertSSLSocket(
+        SSLConfigurationAsserts.assertSSLSocketDefaultConfiguration(
                 (SSLSocket) SSLSocketFactory.getDefault().createSocket());
     }
 
@@ -1458,11 +1475,147 @@
         test.close();
     }
 
-    public void test_SSLSocket_ClientHello_size() throws Exception {
+    public void test_SSLSocket_ClientHello_record_size() throws Exception {
         // This test checks the size of ClientHello of the default SSLSocket. TLS/SSL handshakes
         // with older/unpatched F5/BIG-IP appliances are known to stall and time out when
         // the fragment containing ClientHello is between 256 and 511 (inclusive) bytes long.
-        //
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, null, null);
+        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+        sslSocketFactory = new DelegatingSSLSocketFactory(sslSocketFactory) {
+            @Override
+            protected void configureSocket(SSLSocket socket) {
+                // Enable SNI extension on the socket (this is typically enabled by default)
+                // to increase the size of ClientHello.
+                try {
+                    Method setHostname =
+                            socket.getClass().getMethod("setHostname", String.class);
+                    setHostname.invoke(socket, "sslsockettest.androidcts.google.com");
+                } catch (NoSuchMethodException ignored) {
+                } catch (Exception e) {
+                    throw new RuntimeException("Failed to enable SNI", e);
+                }
+
+                // Enable Session Tickets extension on the socket (this is typically enabled
+                // by default) to increase the size of ClientHello.
+                try {
+                    Method setUseSessionTickets =
+                            socket.getClass().getMethod(
+                                    "setUseSessionTickets", boolean.class);
+                    setUseSessionTickets.invoke(socket, true);
+                } catch (NoSuchMethodException ignored) {
+                } catch (Exception e) {
+                    throw new RuntimeException("Failed to enable Session Tickets", e);
+                }
+            }
+        };
+
+        TlsRecord firstReceivedTlsRecord = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+        assertEquals("TLS record type", TlsProtocols.HANDSHAKE, firstReceivedTlsRecord.type);
+        HandshakeMessage handshakeMessage = HandshakeMessage.read(
+                new DataInputStream(new ByteArrayInputStream(firstReceivedTlsRecord.fragment)));
+        assertEquals("HandshakeMessage type",
+                HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+        int fragmentLength = firstReceivedTlsRecord.fragment.length;
+        if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
+            fail("Fragment containing ClientHello is of dangerous length: "
+                    + fragmentLength + " bytes");
+        }
+    }
+
+    public void test_SSLSocket_ClientHello_cipherSuites() throws Exception {
+        ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+                String[] cipherSuites = new String[clientHello.cipherSuites.size()];
+                for (int i = 0; i < clientHello.cipherSuites.size(); i++) {
+                    CipherSuite cipherSuite = clientHello.cipherSuites.get(i);
+                    cipherSuites[i] = cipherSuite.getAndroidName();
+                }
+                StandardNames.assertDefaultCipherSuites(cipherSuites);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception {
+        ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+                assertEquals(TlsProtocolVersion.TLSv1_2, clientHello.clientVersion);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    public void test_SSLSocket_ClientHello_compressionMethods() throws Exception {
+        ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+                assertEquals(Arrays.asList(CompressionMethod.NULL), clientHello.compressionMethods);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    public void test_SSLSocket_ClientHello_SNI() throws Exception {
+        ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+                ServerNameHelloExtension sniExtension = (ServerNameHelloExtension)
+                        clientHello.findExtensionByType(HelloExtension.TYPE_SERVER_NAME);
+                assertNotNull(sniExtension);
+                assertEquals(Arrays.asList("localhost.localdomain"), sniExtension.hostnames);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    private List<Pair<String, SSLSocketFactory>> getSSLSocketFactoriesToTest()
+            throws NoSuchAlgorithmException, KeyManagementException {
+        List<Pair<String, SSLSocketFactory>> result =
+                new ArrayList<Pair<String, SSLSocketFactory>>();
+        result.add(Pair.of("default", (SSLSocketFactory) SSLSocketFactory.getDefault()));
+        for (String sslContextProtocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            SSLContext sslContext = SSLContext.getInstance(sslContextProtocol);
+            if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(sslContextProtocol)) {
+                continue;
+            }
+            sslContext.init(null, null, null);
+            result.add(Pair.of(
+                    "SSLContext(\"" + sslContext.getProtocol() + "\")",
+                    sslContext.getSocketFactory()));
+        }
+        return result;
+    }
+
+    private ClientHello captureTlsHandshakeClientHello(SSLSocketFactory sslSocketFactory)
+            throws Exception {
+        TlsRecord record = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+        assertEquals("TLS record type", TlsProtocols.HANDSHAKE, record.type);
+        ByteArrayInputStream fragmentIn = new ByteArrayInputStream(record.fragment);
+        HandshakeMessage handshakeMessage = HandshakeMessage.read(new DataInputStream(fragmentIn));
+        assertEquals("HandshakeMessage type",
+                HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+        // Assert that the fragment does not contain any more messages
+        assertEquals(0, fragmentIn.available());
+
+        return (ClientHello) handshakeMessage;
+    }
+
+    private TlsRecord captureTlsHandshakeFirstTlsRecord(SSLSocketFactory sslSocketFactory)
+            throws Exception {
+        byte[] firstReceivedChunk = captureTlsHandshakeFirstTransmittedChunkBytes(sslSocketFactory);
+        ByteArrayInputStream firstReceivedChunkIn = new ByteArrayInputStream(firstReceivedChunk);
+        TlsRecord record = TlsRecord.read(new DataInputStream(firstReceivedChunkIn));
+        // Assert that the chunk does not contain any more data
+        assertEquals(0, firstReceivedChunkIn.available());
+
+        return record;
+    }
+
+    private byte[] captureTlsHandshakeFirstTransmittedChunkBytes(
+            final SSLSocketFactory sslSocketFactory) throws Exception {
         // Since there's no straightforward way to obtain a ClientHello from SSLSocket, this test
         // does the following:
         // 1. Creates a listening server socket (a plain one rather than a TLS/SSL one).
@@ -1506,33 +1659,19 @@
             executorService.submit(new Callable<Void>() {
                 @Override
                 public Void call() throws Exception {
-                    SSLContext sslContext = SSLContext.getInstance("TLS");
-                    sslContext.init(null, null, null);
-                    SSLSocket client = (SSLSocket) sslContext.getSocketFactory().createSocket();
+                    Socket client = new Socket();
                     sockets[0] = client;
                     try {
-                        // Enable SNI extension on the socket (this is typically enabled by default)
-                        // to increase the size of ClientHello.
-                        try {
-                            Method setHostname =
-                                    client.getClass().getMethod("setHostname", String.class);
-                            setHostname.invoke(client, "sslsockettest.androidcts.google.com");
-                        } catch (NoSuchMethodException ignored) {}
-
-                        // Enable Session Tickets extension on the socket (this is typically enabled
-                        // by default) to increase the size of ClientHello.
-                        try {
-                            Method setUseSessionTickets =
-                                    client.getClass().getMethod(
-                                            "setUseSessionTickets", boolean.class);
-                            setUseSessionTickets.invoke(client, true);
-                        } catch (NoSuchMethodException ignored) {}
-
                         client.connect(finalListeningSocket.getLocalSocketAddress());
                         // Initiate the TLS/SSL handshake which is expected to fail as soon as the
                         // server socket receives a ClientHello.
                         try {
-                            client.startHandshake();
+                            SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
+                                    client,
+                                    "localhost.localdomain",
+                                    finalListeningSocket.getLocalPort(),
+                                    true);
+                            sslSocket.startHandshake();
                             fail();
                             return null;
                         } catch (IOException expected) {}
@@ -1551,16 +1690,7 @@
             });
 
             // Wait for the ClientHello to arrive
-            byte[] clientHello = readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
-
-            // Check for ClientHello length that may cause handshake to fail/time out with older
-            // F5/BIG-IP appliances.
-            assertEquals("TLS record type: handshake", 22, clientHello[0]);
-            int fragmentLength = ((clientHello[3] & 0xff) << 8) | (clientHello[4] & 0xff);
-            if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
-                fail("Fragment containing ClientHello is of dangerous length: "
-                        + fragmentLength + " bytes");
-            }
+            return readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
         } finally {
             executorService.shutdownNow();
             IoUtils.closeQuietly(listeningSocket);
diff --git a/luni/src/test/java/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/net/MimeUtilsTest.java
index 9bfb375..ff22632 100644
--- a/luni/src/test/java/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/net/MimeUtilsTest.java
@@ -27,6 +27,12 @@
     assertEquals("flac", MimeUtils.guessExtensionFromMimeType("application/x-flac"));
   }
 
+  // https://code.google.com/p/android/issues/detail?id=78909
+  public void test_78909() {
+    assertEquals("mka", MimeUtils.guessExtensionFromMimeType("audio/x-matroska"));
+    assertEquals("mkv", MimeUtils.guessExtensionFromMimeType("video/x-matroska"));
+  }
+
   public void test_16978217() {
     assertEquals("image/x-ms-bmp", MimeUtils.guessMimeTypeFromExtension("bmp"));
     assertEquals("image/x-icon", MimeUtils.guessMimeTypeFromExtension("ico"));
diff --git a/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java
new file mode 100644
index 0000000..d04e2ba
--- /dev/null
+++ b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.net;
+
+import junit.framework.TestCase;
+import libcore.io.IoUtils;
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.ErrorManager;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.SocketHandler;
+
+public class NetworkSecurityPolicyTest extends TestCase {
+
+    private boolean mCleartextTrafficPermittedOriginalState;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCleartextTrafficPermittedOriginalState =
+                NetworkSecurityPolicy.isCleartextTrafficPermitted();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            NetworkSecurityPolicy.setCleartextTrafficPermitted(
+                    mCleartextTrafficPermittedOriginalState);
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    public void testCleartextTrafficPolicySetterAndGetter() {
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+        assertEquals(false, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+        assertEquals(true, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+        assertEquals(false, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+        assertEquals(true, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+    }
+
+    public void testCleartextTrafficPolicyWithHttpURLConnection() throws Exception {
+        // Assert that client transmits some data when cleartext traffic is permitted.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+        try (CapturingServerSocket server = new CapturingServerSocket()) {
+            URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
+            try {
+                url.openConnection().getContent();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertDataTransmittedByClient();
+        }
+
+        // Assert that client does not transmit any data when cleartext traffic is not permitted and
+        // that URLConnection.openConnection or getContent fail with an IOException.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+        try (CapturingServerSocket server = new CapturingServerSocket()) {
+            URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
+            try {
+                url.openConnection().getContent();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertNoDataTransmittedByClient();
+        }
+    }
+
+    public void testCleartextTrafficPolicyWithFtpURLConnection() throws Exception {
+        // Assert that client transmits some data when cleartext traffic is permitted.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+        byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
+        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+            URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
+            try {
+                url.openConnection().getContent();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertDataTransmittedByClient();
+        }
+
+        // Assert that client does not transmit any data when cleartext traffic is not permitted and
+        // that URLConnection.openConnection or getContent fail with an IOException.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+            URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
+            try {
+                url.openConnection().getContent();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertNoDataTransmittedByClient();
+        }
+    }
+
+    public void testCleartextTrafficPolicyWithJarHttpURLConnection() throws Exception {
+        // Assert that client transmits some data when cleartext traffic is permitted.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+        try (CapturingServerSocket server = new CapturingServerSocket()) {
+            URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
+            try {
+                ((JarURLConnection) url.openConnection()).getManifest();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertDataTransmittedByClient();
+        }
+
+        // Assert that client does not transmit any data when cleartext traffic is not permitted and
+        // that JarURLConnection.openConnection or getManifest fail with an IOException.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+        try (CapturingServerSocket server = new CapturingServerSocket()) {
+            URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
+            try {
+                ((JarURLConnection) url.openConnection()).getManifest();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertNoDataTransmittedByClient();
+        }
+    }
+
+    public void testCleartextTrafficPolicyWithJarFtpURLConnection() throws Exception {
+        // Assert that client transmits some data when cleartext traffic is permitted.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+        byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
+        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+            URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
+            try {
+                ((JarURLConnection) url.openConnection()).getManifest();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertDataTransmittedByClient();
+        }
+
+        // Assert that client does not transmit any data when cleartext traffic is not permitted and
+        // that JarURLConnection.openConnection or getManifest fail with an IOException.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+            URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
+            try {
+                ((JarURLConnection) url.openConnection()).getManifest();
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertNoDataTransmittedByClient();
+        }
+    }
+
+    public void testCleartextTrafficPolicyWithLoggingSocketHandler() throws Exception {
+        // Assert that client transmits some data when cleartext traffic is permitted.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+        try (CapturingServerSocket server = new CapturingServerSocket()) {
+            SocketHandler logger = new SocketHandler("localhost", server.getPort());
+            MockErrorManager mockErrorManager = new MockErrorManager();
+            logger.setErrorManager(mockErrorManager);
+            logger.setLevel(Level.ALL);
+            LogRecord record = new LogRecord(Level.INFO, "A log record");
+            assertTrue(logger.isLoggable(record));
+            logger.publish(record);
+            assertNull(mockErrorManager.getMostRecentException());
+            server.assertDataTransmittedByClient();
+        }
+
+        // Assert that client does not transmit any data when cleartext traffic is not permitted.
+        NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+        try (CapturingServerSocket server = new CapturingServerSocket()) {
+            try {
+                new SocketHandler("localhost", server.getPort());
+                fail();
+            } catch (IOException expected) {
+            }
+            server.assertNoDataTransmittedByClient();
+        }
+    }
+
+    /**
+     * Server socket which listens on a local port and captures the first chunk of data transmitted
+     * by the client.
+     */
+    private static class CapturingServerSocket implements Closeable {
+        private final ServerSocket mSocket;
+        private final int mPort;
+        private final Thread mListeningThread;
+        private final FutureTask<byte[]> mFirstChunkReceivedFuture;
+
+        /**
+         * Constructs a new socket listening on a local port.
+         */
+        public CapturingServerSocket() throws IOException {
+            this(null);
+        }
+
+        /**
+         * Constructs a new socket listening on a local port, which sends the provided reply as
+         * soon as a client connects to it.
+         */
+        public CapturingServerSocket(final byte[] replyOnConnect) throws IOException {
+            mSocket = new ServerSocket(0);
+            mPort = mSocket.getLocalPort();
+            mFirstChunkReceivedFuture = new FutureTask<byte[]>(new Callable<byte[]>() {
+                @Override
+                public byte[] call() throws Exception {
+                    try (Socket client = mSocket.accept()) {
+                        // Reply (if requested)
+                        if (replyOnConnect != null) {
+                            client.getOutputStream().write(replyOnConnect);
+                            client.getOutputStream().flush();
+                        }
+
+                        // Read request
+                        byte[] buf = new byte[64 * 1024];
+                        int chunkSize = client.getInputStream().read(buf);
+                        if (chunkSize == -1) {
+                            // Connection closed without any data received
+                            return new byte[0];
+                        }
+                        // Received some data
+                        return Arrays.copyOf(buf, chunkSize);
+                    } finally {
+                        IoUtils.closeQuietly(mSocket);
+                    }
+                }
+            });
+            mListeningThread = new Thread(mFirstChunkReceivedFuture);
+            mListeningThread.start();
+        }
+
+        public int getPort() {
+            return mPort;
+        }
+
+        public Future<byte[]> getFirstReceivedChunkFuture() {
+            return mFirstChunkReceivedFuture;
+        }
+
+        @Override
+        public void close() {
+            IoUtils.closeQuietly(mSocket);
+            mListeningThread.interrupt();
+        }
+
+        private void assertDataTransmittedByClient()
+                throws Exception {
+            byte[] firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
+            if ((firstChunkFromClient == null) || (firstChunkFromClient.length == 0)) {
+                fail("Client did not transmit any data to server");
+            }
+        }
+
+        private void assertNoDataTransmittedByClient()
+                throws Exception {
+            byte[] firstChunkFromClient;
+            try {
+                firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
+            } catch (TimeoutException expected) {
+                return;
+            }
+            if ((firstChunkFromClient != null) && (firstChunkFromClient.length > 0)) {
+                fail("Client transmitted " + firstChunkFromClient.length+ " bytes: "
+                        + new String(firstChunkFromClient, "US-ASCII"));
+            }
+        }
+    }
+
+    private static class MockErrorManager extends ErrorManager {
+        private Exception mMostRecentException;
+
+        public Exception getMostRecentException() {
+            synchronized (this) {
+                return mMostRecentException;
+            }
+        }
+
+        @Override
+        public void error(String message, Exception exception, int errorCode) {
+            synchronized (this) {
+                mMostRecentException = exception;
+            }
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java b/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java
new file mode 100644
index 0000000..16745ee
--- /dev/null
+++ b/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.net.http;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import junit.framework.TestCase;
+import static libcore.net.http.ResponseUtils.responseCharset;
+
+public class ResponseUtilsTest extends TestCase {
+  public void test_responseCharset_missing() {
+    assertEquals(StandardCharsets.UTF_8, responseCharset(null));
+    assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain"));
+    assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain;foo=bar;baz=bal"));
+    assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain;charset="));
+  }
+
+  public void test_responseCharset_valid() {
+    assertEquals(StandardCharsets.ISO_8859_1,
+            responseCharset("text/plain;charset=ISO-8859-1"));
+    assertEquals(StandardCharsets.ISO_8859_1,
+            responseCharset("text/plain;CHARSET=ISO-8859-1"));
+    assertEquals(StandardCharsets.ISO_8859_1,
+            responseCharset("text/plain;   charset  =   ISO-8859-1"));
+    assertEquals(StandardCharsets.ISO_8859_1,
+            responseCharset("text/plain; foo=bar;baz=bag;charset=ISO-8859-1"));
+    assertEquals(StandardCharsets.ISO_8859_1,
+            responseCharset("text/plain;charset=ISO-8859-1;;==,=="));
+  }
+
+  public void test_responseCharset_invalid() {
+    try {
+      responseCharset("text/plain;charset=unsupportedCharset");
+      fail();
+    } catch (UnsupportedCharsetException expected) {
+    }
+  }
+}
diff --git a/luni/src/test/java/libcore/util/HexEncodingTest.java b/luni/src/test/java/libcore/util/HexEncodingTest.java
new file mode 100644
index 0000000..ef79f5c
--- /dev/null
+++ b/luni/src/test/java/libcore/util/HexEncodingTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.util;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import junit.framework.TestCase;
+import static libcore.util.HexEncoding.decode;
+import static libcore.util.HexEncoding.encode;
+
+public class HexEncodingTest extends TestCase {
+  public void testEncode() {
+    final byte[] avocados = "avocados".getBytes(StandardCharsets.UTF_8);
+
+    assertArraysEqual("61766f6361646f73".toCharArray(), encode(avocados));
+    assertArraysEqual(avocados, decode(encode(avocados), false));
+  }
+
+  public void testDecode_allow4Bit() {
+    assertArraysEqual(new byte[] { 6 }, decode("6".toCharArray(), true));
+    assertArraysEqual(new byte[] { 6, 'v' }, decode("676".toCharArray(), true));
+  }
+
+  public void testDecode_disallow4Bit() {
+    try {
+      decode("676".toCharArray(), false);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testDecode_invalid() {
+    try {
+      decode("deadbard".toCharArray(), false);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    // This demonstrates a difference in behaviour from apache commons : apache
+    // commons uses Character.isDigit and would successfully decode a string with
+    // arabic and devanagari characters.
+    try {
+      decode("६१٧٥٥f6361646f73".toCharArray(), false);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+
+    try {
+      decode("#%6361646f73".toCharArray(), false);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  private static void assertArraysEqual(char[] lhs, char[] rhs) {
+    assertEquals(new String(lhs), new String(rhs));
+  }
+
+  private static void assertArraysEqual(byte[] lhs, byte[] rhs) {
+    assertEquals(Arrays.toString(lhs), Arrays.toString(rhs));
+  }
+}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
index 87b2913..17fb127 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
@@ -28,7 +28,6 @@
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.RSAKeyGenParameterSpec;
-import java.util.Vector;
 
 import javax.crypto.ExemptionMechanism;
 import javax.crypto.ExemptionMechanismException;
@@ -37,7 +36,6 @@
 import javax.crypto.ShortBufferException;
 
 import org.apache.harmony.crypto.tests.support.MyExemptionMechanismSpi;
-import org.apache.harmony.crypto.tests.support.MyExemptionMechanismSpi.tmpKey;
 import org.apache.harmony.security.tests.support.SpiEngUtils;
 
 import junit.framework.TestCase;
@@ -170,45 +168,6 @@
         em.genExemptionBlob(new byte[10], -5);
     }
 
-    static boolean flag = false;
-
-    class Mock_ExemptionMechanism extends  ExemptionMechanism  {
-        protected Mock_ExemptionMechanism(ExemptionMechanismSpi exmechSpi, Provider provider, String mechanism) {
-            super(exmechSpi, provider, mechanism);
-        }
-
-        @Override
-        protected void finalize() {
-            flag = true;
-            super.finalize();
-        }
-    }
-
-    // Side Effect: Causes OutOfMemoryError to test finalization
-    public void test_finalize () {
-        Mock_ExemptionMechanism mem = new Mock_ExemptionMechanism(null, null, "Name");
-        assertNotNull(mem);
-        mem = null;
-        assertFalse(flag);
-        Vector v = new Vector();
-        int capacity;
-        try {
-            while(true) {
-                v.add(this);
-            }
-        } catch (OutOfMemoryError e) {
-            capacity = v.size();
-            v = null;
-        }
-
-        v = new Vector();
-        for (int i = 0; i < capacity/2; i++) {
-            v.add(this);
-        }
-        v = null;
-        assertTrue(flag);
-    }
-
     class Mock_ExemptionMechanismSpi extends MyExemptionMechanismSpi {
         @Override
         protected byte[] engineGenExemptionBlob()
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
index ddd0695..e90452d 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
@@ -100,6 +100,12 @@
         macList.add(Mac.getInstance(defaultAlgorithm, defaultProvider));
         macList.add(Mac.getInstance(defaultAlgorithm, defaultProviderName));
         for (Provider p : Security.getProviders("Mac." + defaultAlgorithm)) {
+            // Do not test AndroidKeyStore's Mac. It cannot be initialized without providing an
+            // AndroidKeyStore-backed SecretKey instance. It's OKish not to test here because it's
+            // tested by cts/tests/test/keystore.
+            if ("AndroidKeyStore".equals(p.getName())) {
+                continue;
+            }
             macList.add(Mac.getInstance(defaultAlgorithm, p));
         }
         return macList.toArray(new Mac[macList.size()]);
@@ -845,6 +851,13 @@
         byte[] output = null;
         byte[] output2 = null;
         for (int i = 0; i < providers.length; i++) {
+            // Do not test AndroidKeyStore's Mac. It cannot be initialized without providing an
+            // AndroidKeyStore-backed SecretKey instance. It's OKish not to test here because it's
+            // tested by cts/tests/test/keystore.
+            if ("AndroidKeyStore".equals(providers[i].getName())) {
+                continue;
+            }
+
             System.out.println("provider = " + providers[i].getName());
             Mac mac = Mac.getInstance("HmacMD5", providers[i]);
             mac.init(key);
@@ -884,6 +897,24 @@
         public abstract void setup();
     }
 
+    public void testMac_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+        Provider mockProvider = new MockProvider("MockProvider") {
+            public void setup() {
+                put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+                put("Mac.FOO SupportedKeyClasses", "None");
+            }
+        };
+
+        Security.addProvider(mockProvider);
+        try {
+            Mac s = Mac.getInstance("FOO", mockProvider);
+            s.init(new MockKey());
+            assertEquals(mockProvider, s.getProvider());
+        } finally {
+            Security.removeProvider(mockProvider.getName());
+        }
+    }
+
     public void testMac_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
         Provider mockProvider = new MockProvider("MockProvider") {
             public void setup() {
diff --git a/luni/src/test/native/libcore_io_Memory_test.cpp b/luni/src/test/native/libcore_io_Memory_test.cpp
new file mode 100644
index 0000000..2d95155
--- /dev/null
+++ b/luni/src/test/native/libcore_io_Memory_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "luni/src/main/native/libcore_io_Memory.cpp"
+
+#include <stdlib.h>
+
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#define ALIGNMENT 8
+
+template<typename T, size_t NUM_ELEMENTS>
+void swap_align_test(void (*swap_func)(T*, const T*, size_t),
+                     std::function<void (T*, T*, uint64_t)> init_func) {
+  uint8_t* dst = nullptr;
+  uint8_t* src = nullptr;
+  ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&dst), ALIGNMENT,
+                              sizeof(T) * NUM_ELEMENTS + ALIGNMENT));
+  ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&src), ALIGNMENT,
+                              sizeof(T) * NUM_ELEMENTS + ALIGNMENT));
+
+  T src_buf[NUM_ELEMENTS];
+  T dst_buf[NUM_ELEMENTS];
+  for (uint64_t i = 0; i < NUM_ELEMENTS; i++) {
+    init_func(&src_buf[i], &dst_buf[i], i);
+  }
+
+  // Vary a few alignments.
+  for (size_t dst_align = 0; dst_align < ALIGNMENT; dst_align++) {
+    T* dst_aligned = reinterpret_cast<T*>(&dst[dst_align]);
+    for (size_t src_align = 0; src_align < ALIGNMENT; src_align++) {
+      T* src_aligned = reinterpret_cast<T*>(&src[src_align]);
+      memset(dst_aligned, 0, sizeof(T) * NUM_ELEMENTS);
+      memcpy(src_aligned, src_buf, sizeof(T) * NUM_ELEMENTS);
+      swap_func(dst_aligned, src_aligned, NUM_ELEMENTS);
+      ASSERT_EQ(0, memcmp(dst_buf, dst_aligned, sizeof(T) * NUM_ELEMENTS))
+          << "Failed at dst align " << dst_align << " src align " << src_align;
+    }
+  }
+  free(dst);
+  free(src);
+}
+
+TEST(libcore, swapShorts_align_test) {
+  // Use an odd number to guarantee that the last 16-bit swap code
+  // is executed.
+  swap_align_test<jshort, 9> (swapShorts, [] (jshort* src, jshort* dst, uint64_t i) {
+    *src = ((2*i) << 8) | (2*(i+1));
+    *dst = (2*i) | ((2*(i+1)) << 8);
+  });
+}
+
+TEST(libcore, swapInts_align_test) {
+  swap_align_test<jint, 10> (swapInts, [] (jint* src, jint* dst, uint64_t i) {
+    *src = ((4*i) << 24) | ((4*(i+1)) << 16) | ((4*(i+2)) << 8) | (4*(i+3));
+    *dst = (4*i) | ((4*(i+1)) << 8) | ((4*(i+2)) << 16) | ((4*(i+3)) << 24);
+  });
+}
+
+TEST(libcore, swapLongs_align_test) {
+  swap_align_test<jlong, 10> (swapLongs, [] (jlong* src, jlong* dst, uint64_t i) {
+    *src = ((8*i) << 56) | ((8*(i+1)) << 48) | ((8*(i+2)) << 40) | ((8*(i+3)) << 32) |
+        ((8*(i+4)) << 24) | ((8*(i+5)) << 16) | ((8*(i+6)) << 8) | (8*(i+7));
+    *dst = (8*i) | ((8*(i+1)) << 8) | ((8*(i+2)) << 16) | ((8*(i+3)) << 24) |
+        ((8*(i+4)) << 32) | ((8*(i+5)) << 40) | ((8*(i+6)) << 48) | ((8*(i+7)) << 56);
+  });
+}
+
+template<typename T>
+void memory_peek_test(T (*peek_func)(JNIEnv*, jclass, jlong), T value) {
+  T* src = nullptr;
+  ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&src), ALIGNMENT,
+                              sizeof(T) + ALIGNMENT));
+  for (size_t i = 0; i < ALIGNMENT; i++) {
+    jlong src_aligned = reinterpret_cast<jlong>(src) + i;
+    memcpy(reinterpret_cast<void*>(src_aligned), &value, sizeof(T));
+    T result = peek_func(nullptr, nullptr, src_aligned);
+    ASSERT_EQ(value, result);
+  }
+  free(src);
+}
+
+TEST(libcore, Memory_peekShortNative_align_check) {
+  memory_peek_test<jshort>(Memory_peekShortNative, 0x0102);
+}
+
+TEST(libcore, Memory_peekIntNative_align_check) {
+  memory_peek_test<jint>(Memory_peekIntNative, 0x01020304);
+}
+
+TEST(libcore, Memory_peekLongNative_align_check) {
+  memory_peek_test<jlong>(Memory_peekLongNative, 0x01020405060708ULL);
+}
+
+template<typename T>
+void memory_poke_test(void (*poke_func)(JNIEnv*, jclass, jlong, T), T value) {
+  T* dst = nullptr;
+  ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&dst), ALIGNMENT,
+                              sizeof(T) + ALIGNMENT));
+  for(size_t i = 0; i < ALIGNMENT; i++) {
+    memset(dst, 0, sizeof(T) + ALIGNMENT);
+    jlong dst_aligned = reinterpret_cast<jlong>(dst) + i;
+    poke_func(nullptr, nullptr, dst_aligned, value);
+    ASSERT_EQ(0, memcmp(reinterpret_cast<void*>(dst_aligned), &value, sizeof(T)));
+  }
+  free(dst);
+}
+
+TEST(libcore, Memory_pokeShortNative_align_check) {
+  memory_poke_test<jshort>(Memory_pokeShortNative, 0x0102);
+}
+
+TEST(libcore, Memory_pokeIntNative_align_check) {
+  memory_poke_test<jint>(Memory_pokeIntNative, 0x01020304);
+}
+
+TEST(libcore, Memory_pokeLongNative_align_check) {
+  memory_poke_test<jlong>(Memory_pokeLongNative, 0x0102030405060708ULL);
+}
diff --git a/luni/src/test/native/test_openssl_engine.cpp b/luni/src/test/native/test_openssl_engine.cpp
deleted file mode 100644
index 9a0f3b3..0000000
--- a/luni/src/test/native/test_openssl_engine.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.
- */
-
-#include "UniquePtr.h"
-
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/objects.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-
-#define DYNAMIC_ENGINE
-#define TEST_ENGINE_ID   "javacoretests"
-#define TEST_ENGINE_NAME "libcore test engine"
-
-struct RSA_Delete {
-    void operator()(RSA* p) const {
-        RSA_free(p);
-    }
-};
-typedef UniquePtr<RSA, RSA_Delete> Unique_RSA;
-
-static const char* HMAC_TAG = "-HMAC-";
-static const size_t HMAC_TAG_LEN = strlen(HMAC_TAG);
-
-static EVP_PKEY *test_load_key(ENGINE* e, const char *key_id,
-        EVP_PKEY* (*read_func)(BIO*, EVP_PKEY**, pem_password_cb*, void*)) {
-    void* data = static_cast<void*>(const_cast<char*>(key_id));
-
-    EVP_PKEY *key = NULL;
-
-    const size_t key_len = strlen(key_id);
-    if (key_len > HMAC_TAG_LEN && !strncmp(key_id, HMAC_TAG, HMAC_TAG_LEN)) {
-        key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, e, reinterpret_cast<const unsigned char*>(key_id),
-                key_len);
-    } else {
-        BIO* in = BIO_new_mem_buf(data, strlen(key_id));
-        if (!in) {
-            return NULL;
-        }
-        key = read_func(in, NULL, 0, NULL);
-        BIO_free(in);
-
-        if (key != NULL && EVP_PKEY_type(key->type) == EVP_PKEY_RSA) {
-            ENGINE_init(e);
-
-            Unique_RSA rsa(EVP_PKEY_get1_RSA(key));
-            rsa->engine = e;
-            rsa->flags |= RSA_FLAG_EXT_PKEY;
-        }
-    }
-
-    return key;
-}
-
-static EVP_PKEY* test_load_privkey(ENGINE* e, const char* key_id, UI_METHOD*, void*) {
-    return test_load_key(e, key_id, PEM_read_bio_PrivateKey);
-}
-
-static EVP_PKEY* test_load_pubkey(ENGINE* e, const char* key_id, UI_METHOD*, void*) {
-    return test_load_key(e, key_id, PEM_read_bio_PUBKEY);
-}
-
-static const int meths[] = {
-        EVP_PKEY_HMAC,
-};
-
-static int pkey_meths(ENGINE*, EVP_PKEY_METHOD** meth, const int** nids, int nid) {
-    if (nid == EVP_PKEY_HMAC) {
-        *meth = const_cast<EVP_PKEY_METHOD*>(EVP_PKEY_meth_find(nid));
-        return 1;
-    } else if (nid != 0) {
-        return 0;
-    }
-
-    if (nids != NULL) {
-        *nids = meths;
-        return 1;
-    }
-
-    return 0;
-}
-
-static int test_engine_setup(ENGINE* e) {
-    if (!ENGINE_set_id(e, TEST_ENGINE_ID)
-            || !ENGINE_set_name(e, TEST_ENGINE_NAME)
-            || !ENGINE_set_flags(e, 0)
-            || !ENGINE_set_RSA(e, RSA_get_default_method())
-            || !ENGINE_set_load_privkey_function(e, test_load_privkey)
-            || !ENGINE_set_load_pubkey_function(e, test_load_pubkey)
-            || !ENGINE_set_pkey_meths(e, pkey_meths)) {
-        return 0;
-    }
-
-    return 1;
-}
-
-static int test_engine_bind_fn(ENGINE *e, const char *id) {
-    if (id && (strcmp(id, TEST_ENGINE_ID) != 0)) {
-        return 0;
-    }
-
-    if (!test_engine_setup(e)) {
-        return 0;
-    }
-
-    return 1;
-}
-
-extern "C" {
-#undef OPENSSL_EXPORT
-#define OPENSSL_EXPORT extern __attribute__ ((visibility ("default")))
-
-IMPLEMENT_DYNAMIC_CHECK_FN()
-IMPLEMENT_DYNAMIC_BIND_FN(test_engine_bind_fn)
-};
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..1a53312
--- /dev/null
+++ b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
@@ -0,0 +1 @@
+libcore.java.nio.charset.SettableCharsetProvider
diff --git a/run-libcore-tests b/run-libcore-tests
index 79c4d07..fa5a33c 100755
--- a/run-libcore-tests
+++ b/run-libcore-tests
@@ -23,5 +23,7 @@
   --vm-arg -Xmx32M \
   --classpath out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/javalib.jar \
   --classpath out/target/common/obj/JAVA_LIBRARIES/sqlite-jdbc_intermediates/classes.jar \
+  --classpath out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/classes.jar \
+  --classpath out/target/common/obj/JAVA_LIBRARIES/okhttp_intermediates/classes.jar \
   $test_packages \
   || true
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index 528a651..9aab942 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -101,6 +101,9 @@
     public static final Map<String,Set<String>> CIPHER_PADDINGS
             = new HashMap<String,Set<String>>();
 
+    private static final Map<String, String[]> SSL_CONTEXT_PROTOCOLS_ENABLED
+            = new HashMap<String,String[]>();
+
     private static void provide(String type, String algorithm) {
         Set<String> algorithms = PROVIDER_ALGORITHMS.get(type);
         if (algorithms == null) {
@@ -134,6 +137,18 @@
         }
         paddings.addAll(Arrays.asList(newPaddings));
     }
+    private static void provideSslContextEnabledProtocols(String algorithm, TLSVersion minimum,
+            TLSVersion maximum) {
+        if (minimum.ordinal() > maximum.ordinal()) {
+            throw new RuntimeException("TLS version: minimum > maximum");
+        }
+        int versionsLength = maximum.ordinal() - minimum.ordinal() + 1;
+        String[] versionNames = new String[versionsLength];
+        for (int i = 0; i < versionsLength; i++) {
+            versionNames[i] = TLSVersion.values()[i + minimum.ordinal()].name;
+        }
+        SSL_CONTEXT_PROTOCOLS_ENABLED.put(algorithm, versionNames);
+    }
     static {
         provide("AlgorithmParameterGenerator", "DSA");
         provide("AlgorithmParameterGenerator", "DiffieHellman");
@@ -379,6 +394,12 @@
             provide("Signature", "NONEwithRSA");
             provide("Cipher", "RSA/ECB/NOPADDING");
             provide("Cipher", "RSA/ECB/PKCS1PADDING");
+            provide("SecretKeyFactory", "AES");
+            provide("SecretKeyFactory", "HmacSHA1");
+            provide("SecretKeyFactory", "HmacSHA224");
+            provide("SecretKeyFactory", "HmacSHA256");
+            provide("SecretKeyFactory", "HmacSHA384");
+            provide("SecretKeyFactory", "HmacSHA512");
 
             // different names: ARCFOUR vs ARC4
             unprovide("Cipher", "ARCFOUR");
@@ -533,6 +554,24 @@
             }
 
         }
+
+        if (IS_RI) {
+            provideSslContextEnabledProtocols("SSL", TLSVersion.SSLv3, TLSVersion.TLSv1);
+            provideSslContextEnabledProtocols("SSLv3", TLSVersion.SSLv3, TLSVersion.TLSv1);
+            provideSslContextEnabledProtocols("TLS", TLSVersion.SSLv3, TLSVersion.TLSv1);
+            provideSslContextEnabledProtocols("TLSv1", TLSVersion.SSLv3, TLSVersion.TLSv1);
+            provideSslContextEnabledProtocols("TLSv1.1", TLSVersion.SSLv3, TLSVersion.TLSv11);
+            provideSslContextEnabledProtocols("TLSv1.2", TLSVersion.SSLv3, TLSVersion.TLSv12);
+            provideSslContextEnabledProtocols("Default", TLSVersion.SSLv3, TLSVersion.TLSv1);
+        } else {
+            provideSslContextEnabledProtocols("SSL", TLSVersion.SSLv3, TLSVersion.TLSv12);
+            provideSslContextEnabledProtocols("SSLv3", TLSVersion.SSLv3, TLSVersion.TLSv12);
+            provideSslContextEnabledProtocols("TLS", TLSVersion.TLSv1, TLSVersion.TLSv12);
+            provideSslContextEnabledProtocols("TLSv1", TLSVersion.TLSv1, TLSVersion.TLSv12);
+            provideSslContextEnabledProtocols("TLSv1.1", TLSVersion.TLSv1, TLSVersion.TLSv12);
+            provideSslContextEnabledProtocols("TLSv1.2", TLSVersion.TLSv1, TLSVersion.TLSv12);
+            provideSslContextEnabledProtocols("Default", TLSVersion.TLSv1, TLSVersion.TLSv12);
+        }
     }
 
     public static final String SSL_CONTEXT_PROTOCOLS_DEFAULT = "Default";
@@ -571,13 +610,11 @@
         "TLSv1.2"));
     public static final Set<String> SSL_SOCKET_PROTOCOLS_CLIENT_DEFAULT =
             new HashSet<String>(Arrays.asList(
-                "SSLv3",
                 "TLSv1",
                 "TLSv1.1",
                 "TLSv1.2"));
     public static final Set<String> SSL_SOCKET_PROTOCOLS_SERVER_DEFAULT =
             new HashSet<String>(Arrays.asList(
-                "SSLv3",
                 "TLSv1",
                 "TLSv1.1",
                 "TLSv1.2"));
@@ -590,9 +627,26 @@
              * do to disable general use of SSLv2.
              */
             SSL_SOCKET_PROTOCOLS.add("SSLv2Hello");
+
+            /* The RI still has SSLv3 as a default protocol. */
+            SSL_SOCKET_PROTOCOLS_CLIENT_DEFAULT.add("SSLv3");
+            SSL_SOCKET_PROTOCOLS_SERVER_DEFAULT.add("SSLv3");
         }
     }
 
+    private static enum TLSVersion {
+        SSLv3("SSLv3"),
+        TLSv1("TLSv1"),
+        TLSv11("TLSv1.1"),
+        TLSv12("TLSv1.2");
+
+        private final String name;
+
+        TLSVersion(String name) {
+            this.name = name;
+        }
+    };
+
     /**
      * Valid values for X509TrustManager.checkClientTrusted authType,
      * either the algorithm of the public key or UNKNOWN.
@@ -663,14 +717,12 @@
         addBoth(   "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA");
         addBoth(   "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA");
         addBoth(   "TLS_DHE_RSA_WITH_AES_256_CBC_SHA");
-        addBoth(   "TLS_DHE_DSS_WITH_AES_256_CBC_SHA");
         addBoth(   "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA");
         addBoth(   "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
         addBoth(   "TLS_RSA_WITH_AES_128_CBC_SHA");
         addBoth(   "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA");
         addBoth(   "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA");
         addBoth(   "TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
-        addBoth(   "TLS_DHE_DSS_WITH_AES_128_CBC_SHA");
         addBoth(   "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA");
         addBoth(   "TLS_ECDHE_RSA_WITH_RC4_128_SHA");
         addBoth(   "SSL_RSA_WITH_RC4_128_SHA");
@@ -682,7 +734,6 @@
         addBoth(   "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA");
         addBoth(   "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA");
         addBoth(   "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
-        addBoth(   "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA");
         addBoth(   "SSL_RSA_WITH_RC4_128_MD5");
 
         // TLSv1.2 cipher suites
@@ -695,10 +746,6 @@
         addBoth(   "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256");
         addOpenSsl("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256");
         addOpenSsl("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384");
-        addBoth(   "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256");
-        addBoth(   "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256");
-        addOpenSsl("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256");
-        addOpenSsl("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384");
         addBoth(   "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256");
         addBoth(   "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384");
         addOpenSsl("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256");
@@ -753,16 +800,14 @@
         addBoth(   "SSL_RSA_WITH_NULL_MD5");
         addBoth(   "SSL_RSA_WITH_DES_CBC_SHA");
         addBoth(   "SSL_DHE_RSA_WITH_DES_CBC_SHA");
-        addBoth(   "SSL_DHE_DSS_WITH_DES_CBC_SHA");
         addBoth(   "SSL_DH_anon_WITH_DES_CBC_SHA");
         addBoth(   "SSL_RSA_EXPORT_WITH_RC4_40_MD5");
         addBoth(   "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5");
         addBoth(   "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA");
         addBoth(   "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA");
-        addBoth(   "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
         addBoth(   "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA");
 
-        // Android does not have Keberos support
+        // Android does not have Kerberos support
         addRi(     "TLS_KRB5_WITH_RC4_128_SHA");
         addRi(     "TLS_KRB5_WITH_RC4_128_MD5");
         addRi(     "TLS_KRB5_WITH_3DES_EDE_CBC_SHA");
@@ -774,6 +819,17 @@
         addRi(     "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA");
         addRi(     "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5");
 
+        // Android does not have DSS support
+        addRi(     "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
+        addRi(     "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA");
+        addRi(     "SSL_DHE_DSS_WITH_DES_CBC_SHA");
+        addRi(     "TLS_DHE_DSS_WITH_AES_128_CBC_SHA");
+        addRi(     "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256");
+        addNeither("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256");
+        addRi(     "TLS_DHE_DSS_WITH_AES_256_CBC_SHA");
+        addRi(     "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256");
+        addNeither("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384");
+
         // Dropped
         addNeither("SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA");
         addNeither("SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA");
@@ -864,8 +920,6 @@
                             "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                             "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                             "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
-                            "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
-                            "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                             "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
                             "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
                             "TLS_RSA_WITH_AES_128_GCM_SHA256",
@@ -943,7 +997,8 @@
      * suites in a test for those that want to verify separately that
      * all cipher suites were included.
      */
-    public static Set<String> assertValidCipherSuites(Set<String> expected, String[] cipherSuites) {
+    private static Set<String> assertValidCipherSuites(
+            Set<String> expected, String[] cipherSuites) {
         assertNotNull(cipherSuites);
         assertTrue(cipherSuites.length != 0);
 
@@ -965,7 +1020,7 @@
      * assertSupportedCipherSuites additionally verifies that all
      * supported cipher suites where in the input array.
      */
-    public static void assertSupportedCipherSuites(Set<String> expected, String[] cipherSuites) {
+    private static void assertSupportedCipherSuites(Set<String> expected, String[] cipherSuites) {
         Set<String> remainingCipherSuites = assertValidCipherSuites(expected, cipherSuites);
         assertEquals("Missing cipher suites", Collections.EMPTY_SET, remainingCipherSuites);
         assertEquals(expected.size(), cipherSuites.length);
@@ -978,7 +1033,7 @@
      * those that want to verify separately that all protocols were
      * included.
      */
-    public static Set<String> assertValidProtocols(Set<String> expected, String[] protocols) {
+    private static Set<String> assertValidProtocols(Set<String> expected, String[] protocols) {
         assertNotNull(protocols);
         assertTrue(protocols.length != 0);
 
@@ -999,21 +1054,13 @@
      * assertSupportedProtocols additionally verifies that all
      * supported protocols where in the input array.
      */
-    public static void assertSupportedProtocols(Set<String> expected, String[] protocols) {
+    private static void assertSupportedProtocols(Set<String> expected, String[] protocols) {
         Set<String> remainingProtocols = assertValidProtocols(expected, protocols);
         assertEquals("Missing protocols", Collections.EMPTY_SET, remainingProtocols);
         assertEquals(expected.size(), protocols.length);
     }
 
     /**
-     * Asserts that the protocols array is non-null and that all of its contents are supported
-     * protocols.
-     */
-    public static void assertValidProtocols(String[] protocols) {
-        assertValidProtocols(SSL_SOCKET_PROTOCOLS, protocols);
-    }
-
-    /**
      * Asserts that the provided list of protocols matches the supported list of protocols.
      */
     public static void assertSupportedProtocols(String[] protocols) {
@@ -1021,33 +1068,6 @@
     }
 
     /**
-     * Asserts that the protocols array contains all the protocols enabled by default for client use
-     * and no other ones.
-     */
-    public static void assertDefaultProtocolsClient(String[] protocols) {
-        assertValidProtocols(protocols);
-        assertSupportedProtocols(SSL_SOCKET_PROTOCOLS_CLIENT_DEFAULT, protocols);
-    }
-
-    /**
-     * Asserts that the protocols array contains all the protocols enabled by default for server use
-     * and no other ones.
-     */
-    public static void assertDefaultProtocolsServer(String[] protocols) {
-        assertValidProtocols(protocols);
-        assertSupportedProtocols(SSL_SOCKET_PROTOCOLS_SERVER_DEFAULT, protocols);
-    }
-
-    /**
-     * Asserts that the protocols array contains all the protocols enabled by default for
-     * {@link javax.net.ssl.SSLEngine} and no other ones.
-     */
-    public static void assertSSLEngineDefaultProtocols(String[] protocols) {
-        assertValidProtocols(protocols);
-        assertSupportedProtocols(SSL_SOCKET_PROTOCOLS_CLIENT_DEFAULT, protocols);
-    }
-
-    /**
      * Assert that the provided list of cipher suites contains only the supported cipher suites.
      */
     public static void assertValidCipherSuites(String[] cipherSuites) {
@@ -1084,6 +1104,12 @@
         }
     }
 
+    public static void assertSSLContextEnabledProtocols(String version, String[] protocols) {
+        assertEquals("For protocol \"" + version + "\"",
+                Arrays.toString(SSL_CONTEXT_PROTOCOLS_ENABLED.get(version)),
+                Arrays.toString(protocols));
+    }
+
     private static boolean isPermittedDefaultCipherSuite(String cipherSuite) {
         assertNotNull(cipherSuite);
         if (CIPHER_SUITE_SECURE_RENEGOTIATION.equals(cipherSuite)) {
diff --git a/support/src/test/java/libcore/javax/net/ssl/SSLConfigurationAsserts.java b/support/src/test/java/libcore/javax/net/ssl/SSLConfigurationAsserts.java
new file mode 100644
index 0000000..bdaad65
--- /dev/null
+++ b/support/src/test/java/libcore/javax/net/ssl/SSLConfigurationAsserts.java
@@ -0,0 +1,248 @@
+/*
+ * 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.javax.net.ssl;
+
+import junit.framework.Assert;
+import libcore.java.security.StandardNames;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Assertions about the configuration of TLS/SSL primitives.
+ */
+public class SSLConfigurationAsserts extends Assert {
+
+  /** Hidden constructor to prevent instantiation. */
+  private SSLConfigurationAsserts() {}
+
+  /**
+   * Asserts that the provided {@link SSLContext} has the expected default configuration, and that
+   * {@link SSLSocketFactory}, {@link SSLServerSocketFactory}, {@link SSLSocket},
+   * {@link SSLServerSocket} and {@link SSLEngine} instances created from the context match the
+   * configuration.
+   */
+  public static void assertSSLContextDefaultConfiguration(SSLContext sslContext)
+      throws IOException {
+    SSLParameters defaultParameters = sslContext.getDefaultSSLParameters();
+    StandardNames.assertSSLContextEnabledProtocols(sslContext.getProtocol(),
+        defaultParameters.getProtocols());
+    StandardNames.assertDefaultCipherSuites(defaultParameters.getCipherSuites());
+    assertFalse(defaultParameters.getWantClientAuth());
+    assertFalse(defaultParameters.getNeedClientAuth());
+
+    SSLParameters supportedParameters = sslContext.getSupportedSSLParameters();
+    StandardNames.assertSupportedCipherSuites(supportedParameters.getCipherSuites());
+    StandardNames.assertSupportedProtocols(supportedParameters.getProtocols());
+    assertFalse(supportedParameters.getWantClientAuth());
+    assertFalse(supportedParameters.getNeedClientAuth());
+
+    assertContainsAll("Unsupported enabled cipher suites", supportedParameters.getCipherSuites(),
+        defaultParameters.getCipherSuites());
+    assertContainsAll("Unsupported enabled protocols", supportedParameters.getProtocols(),
+        defaultParameters.getProtocols());
+
+    assertSSLSocketFactoryConfigSameAsSSLContext(sslContext.getSocketFactory(), sslContext);
+    assertSSLServerSocketFactoryConfigSameAsSSLContext(sslContext.getServerSocketFactory(),
+        sslContext);
+
+    SSLEngine sslEngine = sslContext.createSSLEngine();
+    assertFalse(sslEngine.getUseClientMode());
+    assertSSLEngineConfigSameAsSSLContext(sslEngine, sslContext);
+  }
+
+  /**
+   * Asserts that the provided {@link SSLSocketFactory} has the expected default configuration and
+   * that {@link SSLSocket} instances created by the factory match the configuration.
+   */
+  public static void assertSSLSocketFactoryDefaultConfiguration(
+      SSLSocketFactory sslSocketFactory) throws Exception {
+    assertSSLSocketFactoryConfigSameAsSSLContext(sslSocketFactory,
+        SSLContext.getDefault());
+  }
+
+  /**
+   * Asserts that {@link SSLSocketFactory}'s configuration matches {@code SSLContext}'s
+   * configuration, and that {@link SSLSocket} instances obtained from the factory match this
+   * configuration as well.
+   */
+  private static void assertSSLSocketFactoryConfigSameAsSSLContext(
+      SSLSocketFactory sslSocketFactory, SSLContext sslContext) throws IOException {
+    assertCipherSuitesEqual(sslContext.getDefaultSSLParameters().getCipherSuites(),
+        sslSocketFactory.getDefaultCipherSuites());
+    assertCipherSuitesEqual(sslContext.getSupportedSSLParameters().getCipherSuites(),
+        sslSocketFactory.getSupportedCipherSuites());
+
+    try (SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket()) {
+      assertTrue(sslSocket.getUseClientMode());
+      assertTrue(sslSocket.getEnableSessionCreation());
+      assertSSLSocketConfigSameAsSSLContext(sslSocket, sslContext);
+    }
+  }
+
+  /**
+   * Asserts that the provided {@link SSLSocket} has the expected default configuration.
+   */
+  public static void assertSSLSocketDefaultConfiguration(SSLSocket sslSocket) throws Exception {
+    assertTrue(sslSocket.getUseClientMode());
+    assertTrue(sslSocket.getEnableSessionCreation());
+    assertSSLSocketConfigSameAsSSLContext(sslSocket, SSLContext.getDefault());
+  }
+
+  /**
+   * Asserts that {@link SSLSocket}'s configuration matches {@code SSLContext's} configuration.
+   */
+  private static void assertSSLSocketConfigSameAsSSLContext(SSLSocket sslSocket,
+      SSLContext sslContext) {
+    assertSSLParametersEqual(sslSocket.getSSLParameters(), sslContext.getDefaultSSLParameters());
+    assertCipherSuitesEqual(sslSocket.getEnabledCipherSuites(),
+        sslContext.getDefaultSSLParameters().getCipherSuites());
+    assertProtocolsEqual(sslSocket.getEnabledProtocols(),
+        sslContext.getDefaultSSLParameters().getProtocols());
+
+    assertCipherSuitesEqual(sslSocket.getSupportedCipherSuites(),
+        sslContext.getSupportedSSLParameters().getCipherSuites());
+    assertProtocolsEqual(sslSocket.getSupportedProtocols(),
+        sslContext.getSupportedSSLParameters().getProtocols());
+  }
+
+  /**
+   * Asserts that the provided {@link SSLServerSocketFactory} has the expected default
+   * configuration, and that {@link SSLServerSocket} instances created by the factory match the
+   * configuration.
+   */
+  public static void assertSSLServerSocketFactoryDefaultConfiguration(
+      SSLServerSocketFactory sslServerSocketFactory) throws Exception {
+    assertSSLServerSocketFactoryConfigSameAsSSLContext(sslServerSocketFactory,
+        SSLContext.getDefault());
+  }
+
+  /**
+   * Asserts that {@link SSLServerSocketFactory}'s configuration matches {@code SSLContext}'s
+   * configuration, and that {@link SSLServerSocket} instances obtained from the factory match this
+   * configuration as well.
+   */
+  private static void assertSSLServerSocketFactoryConfigSameAsSSLContext(
+      SSLServerSocketFactory sslServerSocketFactory, SSLContext sslContext)  throws IOException {
+    assertCipherSuitesEqual(sslContext.getDefaultSSLParameters().getCipherSuites(),
+        sslServerSocketFactory.getDefaultCipherSuites());
+    assertCipherSuitesEqual(sslContext.getSupportedSSLParameters().getCipherSuites(),
+        sslServerSocketFactory.getSupportedCipherSuites());
+    try (SSLServerSocket sslServerSocket =
+        (SSLServerSocket) sslServerSocketFactory.createServerSocket()) {
+      assertFalse(sslServerSocket.getUseClientMode());
+      assertTrue(sslServerSocket.getEnableSessionCreation());
+      assertSSLServerSocketConfigSameAsSSLContext(sslServerSocket, sslContext);
+    }
+  }
+
+  /**
+   * Asserts that the provided {@link SSLServerSocket} has the expected default configuration.
+   */
+  public static void assertSSLServerSocketDefaultConfiguration(SSLServerSocket sslServerSocket)
+      throws Exception {
+    assertFalse(sslServerSocket.getUseClientMode());
+    assertTrue(sslServerSocket.getEnableSessionCreation());
+    assertSSLServerSocketConfigSameAsSSLContext(sslServerSocket, SSLContext.getDefault());
+    // TODO: Check SSLParameters when supported by SSLServerSocket API
+  }
+
+  /**
+   * Asserts that {@link SSLServerSocket}'s configuration matches {@code SSLContext's}
+   * configuration.
+   */
+  private static void assertSSLServerSocketConfigSameAsSSLContext(SSLServerSocket sslServerSocket,
+      SSLContext sslContext) {
+    assertCipherSuitesEqual(sslServerSocket.getEnabledCipherSuites(),
+        sslContext.getDefaultSSLParameters().getCipherSuites());
+    assertProtocolsEqual(sslServerSocket.getEnabledProtocols(),
+        sslContext.getDefaultSSLParameters().getProtocols());
+
+    assertCipherSuitesEqual(sslServerSocket.getSupportedCipherSuites(),
+        sslContext.getSupportedSSLParameters().getCipherSuites());
+    assertProtocolsEqual(sslServerSocket.getSupportedProtocols(),
+        sslContext.getSupportedSSLParameters().getProtocols());
+
+    assertEquals(sslServerSocket.getNeedClientAuth(),
+        sslContext.getDefaultSSLParameters().getNeedClientAuth());
+    assertEquals(sslServerSocket.getWantClientAuth(),
+        sslContext.getDefaultSSLParameters().getWantClientAuth());
+  }
+
+  /**
+   * Asserts that the provided {@link SSLEngine} has the expected default configuration.
+   */
+  public static void assertSSLEngineDefaultConfiguration(SSLEngine sslEngine) throws Exception {
+    assertFalse(sslEngine.getUseClientMode());
+    assertTrue(sslEngine.getEnableSessionCreation());
+    assertSSLEngineConfigSameAsSSLContext(sslEngine, SSLContext.getDefault());
+  }
+
+  /**
+   * Asserts that {@link SSLEngine}'s configuration matches {@code SSLContext's} configuration.
+   */
+  private static void assertSSLEngineConfigSameAsSSLContext(SSLEngine sslEngine,
+      SSLContext sslContext) {
+    assertSSLParametersEqual(sslEngine.getSSLParameters(), sslContext.getDefaultSSLParameters());
+    assertCipherSuitesEqual(sslEngine.getEnabledCipherSuites(),
+        sslContext.getDefaultSSLParameters().getCipherSuites());
+    assertProtocolsEqual(sslEngine.getEnabledProtocols(),
+        sslContext.getDefaultSSLParameters().getProtocols());
+
+    assertCipherSuitesEqual(sslEngine.getSupportedCipherSuites(),
+        sslContext.getSupportedSSLParameters().getCipherSuites());
+    assertProtocolsEqual(sslEngine.getSupportedProtocols(),
+        sslContext.getSupportedSSLParameters().getProtocols());
+  }
+
+  private static void assertSSLParametersEqual(SSLParameters expected, SSLParameters actual) {
+    assertCipherSuitesEqual(expected.getCipherSuites(), actual.getCipherSuites());
+    assertProtocolsEqual(expected.getProtocols(), actual.getProtocols());
+    assertEquals(expected.getNeedClientAuth(), actual.getNeedClientAuth());
+    assertEquals(expected.getWantClientAuth(), actual.getWantClientAuth());
+  }
+
+  private static void assertCipherSuitesEqual(String[] expected, String[] actual) {
+    assertEquals(Arrays.asList(expected), Arrays.asList(actual));
+  }
+
+  private static void assertProtocolsEqual(String[] expected, String[] actual) {
+    // IMPLEMENTATION NOTE: The order of protocols versions does not matter. Similarly, it only
+    // matters whether a protocol version is present or absent in the array. These arrays are
+    // supposed to represent sets of protocol versions. Thus, we treat them as such.
+    assertEquals(new HashSet<String>(Arrays.asList(expected)),
+        new HashSet<String>(Arrays.asList(actual)));
+  }
+
+  /**
+   * Asserts that the {@code container} contains all the {@code elements}.
+   */
+  private static void assertContainsAll(String message, String[] container, String[] elements) {
+    Set<String> elementsNotInContainer = new HashSet<String>(Arrays.asList(elements));
+    elementsNotInContainer.removeAll(Arrays.asList(container));
+    assertEquals(message, Collections.EMPTY_SET, elementsNotInContainer);
+  }
+}
diff --git a/support/src/test/java/libcore/javax/net/ssl/SSLDefaultConfigurationAsserts.java b/support/src/test/java/libcore/javax/net/ssl/SSLDefaultConfigurationAsserts.java
deleted file mode 100644
index d54f5e5..0000000
--- a/support/src/test/java/libcore/javax/net/ssl/SSLDefaultConfigurationAsserts.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.javax.net.ssl;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import junit.framework.Assert;
-import libcore.java.security.StandardNames;
-
-/**
- * Assertions about the default configuration of TLS/SSL primitives.
- */
-public abstract class SSLDefaultConfigurationAsserts extends Assert {
-
-  /** Hidden constructor to prevent instantiation. */
-  private SSLDefaultConfigurationAsserts() {}
-
-  /**
-   * Asserts that the provided {@link SSLContext} has the expected default configuration.
-   */
-  public static void assertSSLContext(SSLContext sslContext) throws IOException {
-    assertDefaultSSLParametersClient(sslContext.getDefaultSSLParameters());
-    assertSupportedSSLParametersClient(sslContext.getSupportedSSLParameters());
-    assertSSLSocketFactory(sslContext.getSocketFactory());
-    assertSSLServerSocketFactory(sslContext.getServerSocketFactory());
-    assertSSLEngine(sslContext.createSSLEngine());
-    assertSSLEngine(sslContext.createSSLEngine(null, -1));
-  }
-
-  /**
-   * Asserts that the provided {@link SSLSocketFactory} has the expected default configuration.
-   */
-  public static void assertSSLSocketFactory(SSLSocketFactory sslSocketFactory) throws IOException {
-    StandardNames.assertDefaultCipherSuites(sslSocketFactory.getDefaultCipherSuites());
-    StandardNames.assertSupportedCipherSuites(sslSocketFactory.getSupportedCipherSuites());
-    assertContainsAll("Unsupported default cipher suites",
-        sslSocketFactory.getSupportedCipherSuites(),
-        sslSocketFactory.getDefaultCipherSuites());
-
-    assertSSLSocket((SSLSocket) sslSocketFactory.createSocket());
-  }
-
-  /**
-   * Asserts that the provided {@link SSLServerSocketFactory} has the expected default
-   * configuration.
-   */
-  public static void assertSSLServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory)
-      throws IOException {
-    StandardNames.assertDefaultCipherSuites(sslServerSocketFactory.getDefaultCipherSuites());
-    StandardNames.assertSupportedCipherSuites(sslServerSocketFactory.getSupportedCipherSuites());
-    assertContainsAll("Unsupported default cipher suites",
-        sslServerSocketFactory.getSupportedCipherSuites(),
-        sslServerSocketFactory.getDefaultCipherSuites());
-
-    assertSSLServerSocket((SSLServerSocket) sslServerSocketFactory.createServerSocket());
-  }
-
-  /**
-   * Asserts that the provided {@link SSLSocket} has the expected default configuration.
-   */
-  public static void assertSSLSocket(SSLSocket sslSocket) {
-    assertSSLParametersClient(sslSocket.getSSLParameters());
-
-    StandardNames.assertDefaultCipherSuites(sslSocket.getEnabledCipherSuites());
-    StandardNames.assertSupportedCipherSuites(sslSocket.getSupportedCipherSuites());
-    assertContainsAll("Unsupported enabled cipher suites",
-        sslSocket.getSupportedCipherSuites(),
-        sslSocket.getEnabledCipherSuites());
-
-    StandardNames.assertDefaultProtocolsClient(sslSocket.getEnabledProtocols());
-    StandardNames.assertSupportedProtocols(sslSocket.getSupportedProtocols());
-    assertContainsAll("Unsupported enabled protocols",
-        sslSocket.getSupportedProtocols(),
-        sslSocket.getEnabledProtocols());
-
-    assertTrue(sslSocket.getUseClientMode());
-    assertTrue(sslSocket.getEnableSessionCreation());
-    assertFalse(sslSocket.getNeedClientAuth());
-    assertFalse(sslSocket.getWantClientAuth());
-  }
-
-  /**
-   * Asserts that the provided {@link SSLServerSocket} has the expected default configuration.
-   */
-  public static void assertSSLServerSocket(SSLServerSocket sslServerSocket) {
-    // TODO: Check SSLParameters when supported by SSLServerSocket API
-
-    StandardNames.assertDefaultCipherSuites(sslServerSocket.getEnabledCipherSuites());
-    StandardNames.assertSupportedCipherSuites(sslServerSocket.getSupportedCipherSuites());
-    assertContainsAll("Unsupported enabled cipher suites",
-        sslServerSocket.getSupportedCipherSuites(),
-        sslServerSocket.getEnabledCipherSuites());
-
-    StandardNames.assertDefaultProtocolsServer(sslServerSocket.getEnabledProtocols());
-    StandardNames.assertSupportedProtocols(sslServerSocket.getSupportedProtocols());
-    assertContainsAll("Unsupported enabled protocols",
-        sslServerSocket.getSupportedProtocols(),
-        sslServerSocket.getEnabledProtocols());
-
-    assertTrue(sslServerSocket.getEnableSessionCreation());
-    assertFalse(sslServerSocket.getNeedClientAuth());
-    assertFalse(sslServerSocket.getWantClientAuth());
-  }
-
-  /**
-   * Asserts that the provided {@link SSLEngine} has the expected default configuration.
-   */
-  public static void assertSSLEngine(SSLEngine sslEngine) {
-    assertFalse(sslEngine.getUseClientMode());
-    assertSSLEngineSSLParameters(sslEngine.getSSLParameters());
-
-    StandardNames.assertDefaultCipherSuites(sslEngine.getEnabledCipherSuites());
-    StandardNames.assertSupportedCipherSuites(sslEngine.getSupportedCipherSuites());
-    assertContainsAll("Unsupported enabled cipher suites",
-        sslEngine.getSupportedCipherSuites(),
-        sslEngine.getEnabledCipherSuites());
-
-    StandardNames.assertSSLEngineDefaultProtocols(sslEngine.getEnabledProtocols());
-    StandardNames.assertSupportedProtocols(sslEngine.getSupportedProtocols());
-    assertContainsAll("Unsupported enabled protocols",
-        sslEngine.getSupportedProtocols(),
-        sslEngine.getEnabledProtocols());
-
-    assertTrue(sslEngine.getEnableSessionCreation());
-    assertFalse(sslEngine.getNeedClientAuth());
-    assertFalse(sslEngine.getWantClientAuth());
-  }
-
-  /**
-   * Asserts that the provided {@link SSLParameters} describe the expected default configuration
-   * for client-side mode of operation.
-   */
-  public static void assertSSLParametersClient(SSLParameters sslParameters) {
-    assertDefaultSSLParametersClient(sslParameters);
-  }
-
-  /**
-   * Asserts that the provided default {@link SSLParameters} are as expected for client-side mode of
-   * operation.
-   */
-  private static void assertDefaultSSLParametersClient(SSLParameters sslParameters) {
-    StandardNames.assertDefaultCipherSuites(sslParameters.getCipherSuites());
-    StandardNames.assertDefaultProtocolsClient(sslParameters.getProtocols());
-    assertFalse(sslParameters.getWantClientAuth());
-    assertFalse(sslParameters.getNeedClientAuth());
-  }
-
-  /**
-   * Asserts that the provided supported {@link SSLParameters} are as expected for client-side mode
-   * of operation.
-   */
-  private static void assertSupportedSSLParametersClient(SSLParameters sslParameters) {
-    StandardNames.assertSupportedCipherSuites(sslParameters.getCipherSuites());
-    StandardNames.assertSupportedProtocols(sslParameters.getProtocols());
-    assertFalse(sslParameters.getWantClientAuth());
-    assertFalse(sslParameters.getNeedClientAuth());
-  }
-
-  /**
-   * Asserts that the provided {@link SSLParameters} has the expected default configuration for
-   * {@link SSLEngine}.
-   */
-  public static void assertSSLEngineSSLParameters(SSLParameters sslParameters) {
-    StandardNames.assertDefaultCipherSuites(sslParameters.getCipherSuites());
-    StandardNames.assertSSLEngineDefaultProtocols(sslParameters.getProtocols());
-    assertFalse(sslParameters.getWantClientAuth());
-    assertFalse(sslParameters.getNeedClientAuth());
-  }
-
-  /**
-   * Asserts that the {@code container} contains all the {@code elements}.
-   */
-  private static void assertContainsAll(String message, String[] container, String[] elements) {
-    Set<String> elementsNotInContainer = new HashSet<String>(Arrays.asList(elements));
-    elementsNotInContainer.removeAll(Arrays.asList(container));
-    assertEquals(message, Collections.EMPTY_SET, elementsNotInContainer);
-  }
-}
diff --git a/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java b/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java
index 6c2c943..3a12629 100644
--- a/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java
+++ b/support/src/test/java/libcore/javax/net/ssl/TestSSLEnginePair.java
@@ -21,6 +21,7 @@
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSession;
 import junit.framework.Assert;
 
@@ -134,6 +135,23 @@
         void beforeBeginHandshake(SSLEngine client, SSLEngine server) {}
     }
 
+    public void close() throws SSLException {
+        close(new SSLEngine[] { client, server });
+    }
+
+    public static void close(SSLEngine[] engines) {
+        try {
+            for (SSLEngine engine : engines) {
+                if (engine != null) {
+                    engine.closeInbound();
+                    engine.closeOutbound();
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private static boolean handshakeCompleted(SSLEngine engine,
                                               ByteBuffer output,
                                               ByteBuffer input,
diff --git a/support/src/test/java/libcore/tlswire/handshake/CipherSuite.java b/support/src/test/java/libcore/tlswire/handshake/CipherSuite.java
new file mode 100644
index 0000000..88ce2f2
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/CipherSuite.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.handshake;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@code CipherSuite} enum from TLS 1.2 RFC 5246.
+ */
+public class CipherSuite {
+    // The list of cipher suites below is based on IANA registry
+    // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
+    private static final CipherSuite[] CIPHER_SUITES = new CipherSuite[] {
+        new CipherSuite(0x0000, "TLS_NULL_WITH_NULL_NULL"),
+        new CipherSuite(0x0001, "TLS_RSA_WITH_NULL_MD5", "SSL_RSA_WITH_NULL_MD5"),
+        new CipherSuite(0x0002, "TLS_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_NULL_SHA"),
+        new CipherSuite(0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_RC4_40_MD5"),
+        new CipherSuite(0x0004, "TLS_RSA_WITH_RC4_128_MD5", "SSL_RSA_WITH_RC4_128_MD5"),
+        new CipherSuite(0x0005, "TLS_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_RC4_128_SHA"),
+        new CipherSuite(0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"),
+        new CipherSuite(0x0007, "TLS_RSA_WITH_IDEA_CBC_SHA"),
+        new CipherSuite(0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
+                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+        new CipherSuite(0x0009, "TLS_RSA_WITH_DES_CBC_SHA", "SSL_RSA_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x000a, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x000b, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"),
+        new CipherSuite(0x000c, "TLS_DH_DSS_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x000d, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x000e, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+        new CipherSuite(0x000f, "TLS_DH_RSA_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x0010, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+                "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"),
+        new CipherSuite(0x0012, "TLS_DHE_DSS_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x0013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+                "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+                "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+        new CipherSuite(0x0015, "TLS_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+                "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
+                "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"),
+        new CipherSuite(0x0018, "TLS_DH_anon_WITH_RC4_128_MD5", "SSL_DH_anon_WITH_RC4_128_MD5"),
+        new CipherSuite(0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+                "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"),
+        new CipherSuite(0x001a, "TLS_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x001b, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
+                "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x001e, "TLS_KRB5_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x001f, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x0020, "TLS_KRB5_WITH_RC4_128_SHA"),
+        new CipherSuite(0x0021, "TLS_KRB5_WITH_IDEA_CBC_SHA"),
+        new CipherSuite(0x0022, "TLS_KRB5_WITH_DES_CBC_MD5"),
+        new CipherSuite(0x0023, "TLS_KRB5_WITH_3DES_EDE_CBC_MD5"),
+        new CipherSuite(0x0024, "TLS_KRB5_WITH_RC4_128_MD5"),
+        new CipherSuite(0x0025, "TLS_KRB5_WITH_IDEA_CBC_MD5"),
+        new CipherSuite(0x0026, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"),
+        new CipherSuite(0x0027, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"),
+        new CipherSuite(0x0028, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA"),
+        new CipherSuite(0x0029, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"),
+        new CipherSuite(0x002a, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"),
+        new CipherSuite(0x002b, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5"),
+        new CipherSuite(0x002c, "TLS_PSK_WITH_NULL_SHA"),
+        new CipherSuite(0x002d, "TLS_DHE_PSK_WITH_NULL_SHA"),
+        new CipherSuite(0x002e, "TLS_RSA_PSK_WITH_NULL_SHA"),
+        new CipherSuite(0x002f, "TLS_RSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0030, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0031, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0032, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0033, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0034, "TLS_DH_anon_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0035, "TLS_RSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x0036, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x0037, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x0038, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x0039, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x003a, "TLS_DH_anon_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x003b, "TLS_RSA_WITH_NULL_SHA256"),
+        new CipherSuite(0x003c, "TLS_RSA_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x003d, "TLS_RSA_WITH_AES_256_CBC_SHA256"),
+        new CipherSuite(0x003e, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x003f, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x0040, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x0041, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA"),
+        new CipherSuite(0x0042, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA"),
+        new CipherSuite(0x0043, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA"),
+        new CipherSuite(0x0044, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"),
+        new CipherSuite(0x0045, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"),
+        new CipherSuite(0x0046, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"),
+        new CipherSuite(0x0060, "TLS_RSA_EXPORT1024_WITH_RC4_56_MD5"),
+        new CipherSuite(0x0061, "TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5"),
+        new CipherSuite(0x0062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x0063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA"),
+        new CipherSuite(0x0064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"),
+        new CipherSuite(0x0065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA"),
+        new CipherSuite(0x0066, "TLS_DHE_DSS_WITH_RC4_128_SHA"),
+        new CipherSuite(0x0067, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x0068, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"),
+        new CipherSuite(0x0069, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"),
+        new CipherSuite(0x006a, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"),
+        new CipherSuite(0x006b, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"),
+        new CipherSuite(0x006c, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x006d, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"),
+        new CipherSuite(0x0084, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA"),
+        new CipherSuite(0x0085, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA"),
+        new CipherSuite(0x0086, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA"),
+        new CipherSuite(0x0087, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA"),
+        new CipherSuite(0x0088, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA"),
+        new CipherSuite(0x0089, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA"),
+        new CipherSuite(0x008a, "TLS_PSK_WITH_RC4_128_SHA"),
+        new CipherSuite(0x008b, "TLS_PSK_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x008c, "TLS_PSK_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x008d, "TLS_PSK_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x008e, "TLS_DHE_PSK_WITH_RC4_128_SHA"),
+        new CipherSuite(0x008f, "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x0090, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0091, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x0092, "TLS_RSA_PSK_WITH_RC4_128_SHA"),
+        new CipherSuite(0x0093, "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0x0094, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0x0095, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0x0096, "TLS_RSA_WITH_SEED_CBC_SHA"),
+        new CipherSuite(0x0097, "TLS_DH_DSS_WITH_SEED_CBC_SHA"),
+        new CipherSuite(0x0098, "TLS_DH_RSA_WITH_SEED_CBC_SHA"),
+        new CipherSuite(0x0099, "TLS_DHE_DSS_WITH_SEED_CBC_SHA"),
+        new CipherSuite(0x009a, "TLS_DHE_RSA_WITH_SEED_CBC_SHA"),
+        new CipherSuite(0x009b, "TLS_DH_anon_WITH_SEED_CBC_SHA"),
+        new CipherSuite(0x009c, "TLS_RSA_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x009d, "TLS_RSA_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x009e, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x009f, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00a0, "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x00a1, "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00a2, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x00a3, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00a4, "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x00a5, "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00a6, "TLS_DH_anon_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x00a7, "TLS_DH_anon_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00a8, "TLS_PSK_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x00a9, "TLS_PSK_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00aa, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x00ab, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00ac, "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0x00ad, "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0x00ae, "TLS_PSK_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x00af, "TLS_PSK_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0x00b0, "TLS_PSK_WITH_NULL_SHA256"),
+        new CipherSuite(0x00b1, "TLS_PSK_WITH_NULL_SHA384"),
+        new CipherSuite(0x00b2, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x00b3, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0x00b4, "TLS_DHE_PSK_WITH_NULL_SHA256"),
+        new CipherSuite(0x00b5, "TLS_DHE_PSK_WITH_NULL_SHA384"),
+        new CipherSuite(0x00b6, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0x00b7, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0x00b8, "TLS_RSA_PSK_WITH_NULL_SHA256"),
+        new CipherSuite(0x00b9, "TLS_RSA_PSK_WITH_NULL_SHA384"),
+        new CipherSuite(0x00ba, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0x00bb, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0x00bc, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0x00bd, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0x00be, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0x00bf, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0x00c0, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256"),
+        new CipherSuite(0x00c1, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256"),
+        new CipherSuite(0x00c2, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256"),
+        new CipherSuite(0x00c3, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256"),
+        new CipherSuite(0x00c4, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256"),
+        new CipherSuite(0x00c5, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256"),
+        new CipherSuite(0x00ff, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"),
+        new CipherSuite(0x5600, "TLS_FALLBACK_SCSV"),
+        new CipherSuite(0xc001, "TLS_ECDH_ECDSA_WITH_NULL_SHA"),
+        new CipherSuite(0xc002, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA"),
+        new CipherSuite(0xc003, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc004, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc005, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc006, "TLS_ECDHE_ECDSA_WITH_NULL_SHA"),
+        new CipherSuite(0xc007, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"),
+        new CipherSuite(0xc008, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc009, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc00a, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc00b, "TLS_ECDH_RSA_WITH_NULL_SHA"),
+        new CipherSuite(0xc00c, "TLS_ECDH_RSA_WITH_RC4_128_SHA"),
+        new CipherSuite(0xc00d, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc00e, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc00f, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc010, "TLS_ECDHE_RSA_WITH_NULL_SHA"),
+        new CipherSuite(0xc011, "TLS_ECDHE_RSA_WITH_RC4_128_SHA"),
+        new CipherSuite(0xc012, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc013, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc014, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc015, "TLS_ECDH_anon_WITH_NULL_SHA"),
+        new CipherSuite(0xc016, "TLS_ECDH_anon_WITH_RC4_128_SHA"),
+        new CipherSuite(0xc017, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc018, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc019, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc01a, "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc01b, "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc01c, "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc01d, "TLS_SRP_SHA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc01e, "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc01f, "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc020, "TLS_SRP_SHA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc021, "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc022, "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc023, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0xc024, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0xc025, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0xc026, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0xc027, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0xc028, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0xc029, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0xc02a, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0xc02b, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0xc02c, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0xc02d, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0xc02e, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0xc02f, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0xc030, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0xc031, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"),
+        new CipherSuite(0xc032, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"),
+        new CipherSuite(0xc033, "TLS_ECDHE_PSK_WITH_RC4_128_SHA"),
+        new CipherSuite(0xc034, "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA"),
+        new CipherSuite(0xc035, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"),
+        new CipherSuite(0xc036, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA"),
+        new CipherSuite(0xc037, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"),
+        new CipherSuite(0xc038, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"),
+        new CipherSuite(0xc039, "TLS_ECDHE_PSK_WITH_NULL_SHA"),
+        new CipherSuite(0xc03a, "TLS_ECDHE_PSK_WITH_NULL_SHA256"),
+        new CipherSuite(0xc03b, "TLS_ECDHE_PSK_WITH_NULL_SHA384"),
+        new CipherSuite(0xc03c, "TLS_RSA_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc03d, "TLS_RSA_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc03e, "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc03f, "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc040, "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc041, "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc042, "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc043, "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc044, "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc045, "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc046, "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc047, "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc048, "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc049, "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc04a, "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc04b, "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc04c, "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc04d, "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc04e, "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc04f, "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc050, "TLS_RSA_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc051, "TLS_RSA_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc052, "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc053, "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc054, "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc055, "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc056, "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc057, "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc058, "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc059, "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc05a, "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc05b, "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc05c, "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc05d, "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc05e, "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc05f, "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc060, "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc061, "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc062, "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc063, "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc064, "TLS_PSK_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc065, "TLS_PSK_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc066, "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc067, "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc068, "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc069, "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc06a, "TLS_PSK_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc06b, "TLS_PSK_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc06c, "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc06d, "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc06e, "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256"),
+        new CipherSuite(0xc06f, "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384"),
+        new CipherSuite(0xc070, "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256"),
+        new CipherSuite(0xc071, "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384"),
+        new CipherSuite(0xc072, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc073, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc074, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc075, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc076, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc077, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc078, "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc079, "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc07a, "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc07b, "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc07c, "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc07d, "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc07e, "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc07f, "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc080, "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc081, "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc082, "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc083, "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc084, "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc085, "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc086, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc087, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc088, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc089, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc08a, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc08b, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc08c, "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc08d, "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc08e, "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc08f, "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc090, "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc091, "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc092, "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256"),
+        new CipherSuite(0xc093, "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384"),
+        new CipherSuite(0xc094, "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc095, "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc096, "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc097, "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc098, "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc099, "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc09a, "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+        new CipherSuite(0xc09b, "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+        new CipherSuite(0xc09c, "TLS_RSA_WITH_AES_128_CCM"),
+        new CipherSuite(0xc09d, "TLS_RSA_WITH_AES_256_CCM"),
+        new CipherSuite(0xc09e, "TLS_DHE_RSA_WITH_AES_128_CCM"),
+        new CipherSuite(0xc09f, "TLS_DHE_RSA_WITH_AES_256_CCM"),
+        new CipherSuite(0xc0a0, "TLS_RSA_WITH_AES_128_CCM_8"),
+        new CipherSuite(0xc0a1, "TLS_RSA_WITH_AES_256_CCM_8"),
+        new CipherSuite(0xc0a2, "TLS_DHE_RSA_WITH_AES_128_CCM_8"),
+        new CipherSuite(0xc0a3, "TLS_DHE_RSA_WITH_AES_256_CCM_8"),
+        new CipherSuite(0xc0a4, "TLS_PSK_WITH_AES_128_CCM"),
+        new CipherSuite(0xc0a5, "TLS_PSK_WITH_AES_256_CCM"),
+        new CipherSuite(0xc0a6, "TLS_DHE_PSK_WITH_AES_128_CCM"),
+        new CipherSuite(0xc0a7, "TLS_DHE_PSK_WITH_AES_256_CCM"),
+        new CipherSuite(0xc0a8, "TLS_PSK_WITH_AES_128_CCM_8"),
+        new CipherSuite(0xc0a9, "TLS_PSK_WITH_AES_256_CCM_8"),
+        new CipherSuite(0xc0aa, "TLS_PSK_DHE_WITH_AES_128_CCM_8"),
+        new CipherSuite(0xc0ab, "TLS_PSK_DHE_WITH_AES_256_CCM_8"),
+        new CipherSuite(0xc0ac, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"),
+        new CipherSuite(0xc0ad, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM"),
+        new CipherSuite(0xc0ae, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"),
+        new CipherSuite(0xc0af, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"),
+        new CipherSuite(0xcc13, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"),
+        new CipherSuite(0xcc14, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"),
+        new CipherSuite(0xcc15, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305"),
+    };
+
+    private static final Map<Integer, CipherSuite> CODE_TO_CIPHER_SUITE;
+    private static final Map<String, CipherSuite> NAME_TO_CIPHER_SUITE;
+
+
+    static {
+        Map<Integer, CipherSuite> byCode = new HashMap<Integer, CipherSuite>();
+        Map<String, CipherSuite> byName = new HashMap<String, CipherSuite>();
+        for (CipherSuite cipherSuite : CIPHER_SUITES) {
+            if (byCode.put(cipherSuite.code, cipherSuite) != null) {
+                throw new RuntimeException(
+                        "Cipher suite multiply defined: " + Integer.toHexString(cipherSuite.code));
+            }
+            String name = cipherSuite.name;
+            if (byName.put(name, cipherSuite) != null) {
+                throw new RuntimeException(
+                        "Cipher suite multiply defined: " + cipherSuite.name);
+            }
+            String androidName = cipherSuite.getAndroidName();
+            if (!name.equals(androidName)) {
+                if (byName.put(androidName, cipherSuite) != null) {
+                    throw new RuntimeException(
+                            "Cipher suite multiply defined: " + cipherSuite.androidName);
+                }
+            }
+        }
+
+        CODE_TO_CIPHER_SUITE = byCode;
+        NAME_TO_CIPHER_SUITE = byName;
+    }
+
+    public final int code;
+    public final String name;
+    private final String androidName;
+
+    private CipherSuite(int code, String name) {
+        this.code = code;
+        this.name = name;
+        this.androidName = null;
+    }
+
+    private CipherSuite(int code, String name, String androidName) {
+        this.code = code;
+        this.name = name;
+        this.androidName = androidName;
+    }
+
+    public static CipherSuite valueOf(String name) {
+        CipherSuite result = NAME_TO_CIPHER_SUITE.get(name);
+        if (result != null) {
+            return result;
+        }
+        throw new IllegalArgumentException("Unknown cipher suite: " + name);
+    }
+
+    public static CipherSuite valueOf(int code) {
+        CipherSuite result = CODE_TO_CIPHER_SUITE.get(code);
+        if (result != null) {
+            return result;
+        }
+        return new CipherSuite(code, Integer.toHexString(code));
+    }
+
+    public String getAndroidName() {
+        return (androidName != null) ? androidName : name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + code;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        CipherSuite other = (CipherSuite) obj;
+        if (code != other.code) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/support/src/test/java/libcore/tlswire/handshake/ClientHello.java b/support/src/test/java/libcore/tlswire/handshake/ClientHello.java
new file mode 100644
index 0000000..ec88662
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/ClientHello.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.handshake;
+
+import libcore.tlswire.util.TlsProtocolVersion;
+import libcore.tlswire.util.IoUtils;
+import libcore.util.HexEncoding;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link ClientHello} {@link HandshakeMessage} from TLS 1.2 RFC 5246.
+ */
+public class ClientHello extends HandshakeMessage {
+    public TlsProtocolVersion clientVersion;
+    public byte[] random;
+    public byte[] sessionId;
+    public List<CipherSuite> cipherSuites;
+    public List<CompressionMethod> compressionMethods;
+
+    /** Extensions or {@code null} for no extensions. */
+    public List<HelloExtension> extensions;
+
+    @Override
+    protected void parseBody(DataInput in) throws IOException {
+        clientVersion = TlsProtocolVersion.read(in);
+        random = new byte[32];
+        in.readFully(random);
+        sessionId = IoUtils.readTlsVariableLengthByteVector(in, 32);
+        int[] cipherSuiteCodes = IoUtils.readTlsVariableLengthUnsignedShortVector(in, 0xfffe);
+        cipherSuites = new ArrayList<CipherSuite>(cipherSuiteCodes.length);
+        for (int i = 0; i < cipherSuiteCodes.length; i++) {
+            cipherSuites.add(CipherSuite.valueOf(cipherSuiteCodes[i]));
+        }
+        byte[] compressionMethodCodes = IoUtils.readTlsVariableLengthByteVector(in, 0xff);
+        compressionMethods = new ArrayList<CompressionMethod>(compressionMethodCodes.length);
+        for (int i = 0; i < compressionMethodCodes.length; i++) {
+            int code = compressionMethodCodes[i] & 0xff;
+            compressionMethods.add(CompressionMethod.valueOf(code));
+        }
+
+        int extensionsSectionSize;
+        try {
+            extensionsSectionSize = in.readUnsignedShort();
+        } catch (EOFException e) {
+            // No extensions present
+            extensionsSectionSize = 0;
+        }
+
+        if (extensionsSectionSize > 0) {
+            extensions = new ArrayList<HelloExtension>();
+            byte[] extensionsBytes = new byte[extensionsSectionSize];
+            in.readFully(extensionsBytes);
+            ByteArrayInputStream extensionsIn = new ByteArrayInputStream(extensionsBytes);
+            DataInput extensionsDataIn = new DataInputStream(extensionsIn);
+            while (extensionsIn.available() > 0) {
+                try {
+                    extensions.add(HelloExtension.read(extensionsDataIn));
+                } catch (IOException e) {
+                    throw new IOException(
+                            "Failed to read HelloExtension #" + (extensions.size() + 1));
+                }
+            }
+        }
+    }
+
+    public HelloExtension findExtensionByType(int extensionType) {
+        if (extensions == null) {
+            return null;
+        }
+        for (HelloExtension extension : extensions) {
+            if (extension.type == extensionType) {
+                return extension;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "ClientHello{client version: " + clientVersion
+                + ", random: " + new String(HexEncoding.encode(random))
+                + ", sessionId: " + new String(HexEncoding.encode(sessionId))
+                + ", cipher suites: " + cipherSuites
+                + ", compression methods: " + compressionMethods
+                + ((extensions != null) ? (", extensions: " + String.valueOf(extensions)) : "")
+                + "}";
+    }
+}
diff --git a/support/src/test/java/libcore/tlswire/handshake/CompressionMethod.java b/support/src/test/java/libcore/tlswire/handshake/CompressionMethod.java
new file mode 100644
index 0000000..0f4f619
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/CompressionMethod.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.handshake;
+
+/**
+ * {@code CompressionMethod} enum from TLS 1.2 RFC 5246.
+ */
+public class CompressionMethod {
+
+    public static final CompressionMethod NULL = new CompressionMethod(0, "null");
+    public static final CompressionMethod DEFLATE = new CompressionMethod(1, "deflate");
+
+    public final int type;
+    public final String name;
+
+    private CompressionMethod(int type, String name) {
+        this.type = type;
+        this.name = name;
+    }
+
+    public static CompressionMethod valueOf(int type) {
+        switch (type) {
+            case 0:
+                return NULL;
+            case 1:
+                return DEFLATE;
+            default:
+                return new CompressionMethod(type, String.valueOf(type));
+        }
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + type;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        CompressionMethod other = (CompressionMethod) obj;
+        if (type != other.type) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/support/src/test/java/libcore/tlswire/handshake/HandshakeMessage.java b/support/src/test/java/libcore/tlswire/handshake/HandshakeMessage.java
new file mode 100644
index 0000000..a855b46
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/HandshakeMessage.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.handshake;
+
+import libcore.tlswire.util.IoUtils;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * Handshake Protocol message from TLS 1.2 RFC 5246.
+ */
+public class HandshakeMessage {
+    public static final int TYPE_CLIENT_HELLO = 1;
+
+    public int type;
+    public byte[] body;
+
+    /**
+     * Parses the provided TLS record as a handshake message.
+     */
+    public static HandshakeMessage read(DataInput in) throws IOException {
+        int type = in.readUnsignedByte();
+        HandshakeMessage result;
+        switch (type) {
+            case TYPE_CLIENT_HELLO:
+                result = new ClientHello();
+                break;
+            default:
+                result = new HandshakeMessage();
+                break;
+        }
+        result.type = type;
+        int bodyLength = IoUtils.readUnsignedInt24(in);
+        result.body = new byte[bodyLength];
+        in.readFully(result.body);
+        result.parseBody(new DataInputStream(new ByteArrayInputStream(result.body)));
+        return result;
+    }
+
+    /**
+     * Parses the provided body. The default implementation does nothing.
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    protected void parseBody(@SuppressWarnings("unused") DataInput in) throws IOException {}
+}
diff --git a/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java b/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java
new file mode 100644
index 0000000..5741072
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.handshake;
+
+import libcore.tlswire.util.IoUtils;
+import libcore.util.HexEncoding;
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@code HelloExtension} struct from TLS 1.2 RFC 5246.
+ */
+public class HelloExtension {
+
+    public static final int TYPE_SERVER_NAME = 0;
+    public static final int TYPE_PADDING = 21;
+    public static final int TYPE_SESSION_TICKET = 35;
+
+    private static final Map<Integer, String> TYPE_TO_NAME = new HashMap<Integer, String>();
+    static {
+        TYPE_TO_NAME.put(TYPE_SERVER_NAME, "server_name");
+        TYPE_TO_NAME.put(1, "max_fragment_length");
+        TYPE_TO_NAME.put(2, "client_certificate_url");
+        TYPE_TO_NAME.put(3, "trusted_ca_keys");
+        TYPE_TO_NAME.put(4, "truncated_hmac");
+        TYPE_TO_NAME.put(5, "status_request");
+        TYPE_TO_NAME.put(6, "user_mapping");
+        TYPE_TO_NAME.put(7, "client_authz");
+        TYPE_TO_NAME.put(8, "server_authz");
+        TYPE_TO_NAME.put(9, "cert_type");
+        TYPE_TO_NAME.put(10, "elliptic_curves");
+        TYPE_TO_NAME.put(11, "ec_point_formats");
+        TYPE_TO_NAME.put(12, "srp");
+        TYPE_TO_NAME.put(13, "signature_algorithms");
+        TYPE_TO_NAME.put(14, "use_srtp");
+        TYPE_TO_NAME.put(15, "heartbeat");
+        TYPE_TO_NAME.put(16, "application_layer_protocol_negotiation");
+        TYPE_TO_NAME.put(17, "status_request_v2");
+        TYPE_TO_NAME.put(18, "signed_certificate_timestamp");
+        TYPE_TO_NAME.put(19, "client_certificate_type");
+        TYPE_TO_NAME.put(20, "server_certificate_type");
+        TYPE_TO_NAME.put(TYPE_PADDING, "padding");
+        TYPE_TO_NAME.put(TYPE_SESSION_TICKET, "SessionTicket");
+        TYPE_TO_NAME.put(13172, "next_protocol_negotiation");
+        TYPE_TO_NAME.put(30031, "Channel ID (old)");
+        TYPE_TO_NAME.put(30032, "Channel ID (new)");
+        TYPE_TO_NAME.put(65281, "renegotiation_info");
+    }
+
+    public int type;
+    public String name;
+    public byte[] data;
+
+    public static HelloExtension read(DataInput in) throws IOException {
+        int type = in.readUnsignedShort();
+        HelloExtension result;
+        switch (type) {
+            case TYPE_SERVER_NAME:
+                result = new ServerNameHelloExtension();
+                break;
+            default:
+                result = new HelloExtension();
+                break;
+        }
+        result.type = type;
+        result.name = TYPE_TO_NAME.get(result.type);
+        if (result.name == null) {
+            result.name = String.valueOf(result.type);
+        }
+        result.data = IoUtils.readTlsVariableLengthByteVector(in, 0xffff);
+        result.parseData();
+        return result;
+    }
+
+    /**
+     * @throws IOException
+     */
+    protected void parseData() throws IOException {}
+
+    @Override
+    public String toString() {
+        return "HelloExtension{type: " + name + ", data: " + HexEncoding.encode(data) + "}";
+    }
+}
diff --git a/support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java b/support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java
new file mode 100644
index 0000000..5b06246
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.handshake;
+
+import libcore.tlswire.util.IoUtils;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@code server_name} (SNI) {@link HelloExtension} from TLS 1.2 RFC 5246.
+ */
+public class ServerNameHelloExtension extends HelloExtension {
+    private static final int TYPE_HOST_NAME = 0;
+
+    public List<String> hostnames;
+
+    @Override
+    protected void parseData() throws IOException {
+        byte[] serverNameListBytes = IoUtils.readTlsVariableLengthByteVector(
+                new DataInputStream(new ByteArrayInputStream(data)), 0xffff);
+        ByteArrayInputStream serverNameListIn = new ByteArrayInputStream(serverNameListBytes);
+        DataInputStream in = new DataInputStream(serverNameListIn);
+        hostnames = new ArrayList<String>();
+        while (serverNameListIn.available() > 0) {
+            int type = in.readUnsignedByte();
+            if (type != TYPE_HOST_NAME) {
+                throw new IOException("Unsupported ServerName type: " + type);
+            }
+            byte[] hostnameBytes = IoUtils.readTlsVariableLengthByteVector(in, 0xffff);
+            String hostname = new String(hostnameBytes, "US-ASCII");
+            hostnames.add(hostname);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "HelloExtension{type: server_name, hostnames: " + hostnames + "}";
+    }
+}
diff --git a/support/src/test/java/libcore/tlswire/record/TlsProtocols.java b/support/src/test/java/libcore/tlswire/record/TlsProtocols.java
new file mode 100644
index 0000000..0ce0d35
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/record/TlsProtocols.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.record;
+
+/**
+ * Protocols that can run over the TLS Record Protocol from TLS 1.2 RFC 5246.
+ */
+public class TlsProtocols {
+    public static final int CHANGE_CIPHER_SPEC = 20;
+    public static final int ALERT = 21;
+    public static final int HANDSHAKE = 22;
+    public static final int APPLICATION_DATA = 23;
+    public static final int HEARTBEAT = 24;
+
+    private TlsProtocols() {}
+}
diff --git a/support/src/test/java/libcore/tlswire/record/TlsRecord.java b/support/src/test/java/libcore/tlswire/record/TlsRecord.java
new file mode 100644
index 0000000..1b60407
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/record/TlsRecord.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.record;
+
+import libcore.tlswire.util.TlsProtocolVersion;
+import java.io.DataInput;
+import java.io.IOException;
+
+/**
+ * TLS Record Protocol record from TLS 1.2 RFC 5246.
+ */
+public class TlsRecord {
+    public int type;
+    public TlsProtocolVersion version;
+    public byte[] fragment;
+
+    public static TlsRecord read(DataInput in) throws IOException {
+        TlsRecord result = new TlsRecord();
+        result.type = in.readUnsignedByte();
+        result.version = TlsProtocolVersion.read(in);
+        int fragmentLength = in.readUnsignedShort();
+        result.fragment = new byte[fragmentLength];
+        in.readFully(result.fragment);
+        return result;
+    }
+}
diff --git a/support/src/test/java/libcore/tlswire/util/IoUtils.java b/support/src/test/java/libcore/tlswire/util/IoUtils.java
new file mode 100644
index 0000000..1e2d8f2
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/util/IoUtils.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.util;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+public class IoUtils {
+
+    public static int readUnsignedInt24(DataInput in) throws IOException {
+        return (in.readUnsignedByte() << 16) | in.readUnsignedShort();
+    }
+
+    public static byte[] readTlsVariableLengthByteVector(DataInput in, int maxSizeBytes)
+            throws IOException {
+        int sizeBytes = readTlsVariableLengthVectorSizeBytes(in, maxSizeBytes);
+        byte[] result = new byte[sizeBytes];
+        in.readFully(result);
+        return result;
+    }
+
+    public static int[] readTlsVariableLengthUnsignedShortVector(DataInput in, int maxSizeBytes)
+            throws IOException {
+        int sizeBytes = readTlsVariableLengthVectorSizeBytes(in, maxSizeBytes);
+        int elementCount = sizeBytes / 2;
+        int[] result = new int[elementCount];
+        for (int i = 0; i < elementCount; i++) {
+            result[i] = in.readUnsignedShort();
+        }
+        return result;
+    }
+
+    private static int readTlsVariableLengthVectorSizeBytes(DataInput in, int maxSizeBytes)
+            throws IOException {
+        if (maxSizeBytes < 0x100) {
+            return in.readUnsignedByte();
+        } else if (maxSizeBytes < 0x10000) {
+            return in.readUnsignedShort();
+        } else if (maxSizeBytes < 0x1000000) {
+            return readUnsignedInt24(in);
+        } else {
+            return in.readInt();
+        }
+    }
+}
diff --git a/support/src/test/java/libcore/tlswire/util/TlsProtocolVersion.java b/support/src/test/java/libcore/tlswire/util/TlsProtocolVersion.java
new file mode 100644
index 0000000..f58faf2
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/util/TlsProtocolVersion.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.tlswire.util;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+/**
+ * {@code ProtovolVersion} struct from TLS 1.2 RFC 5246.
+ */
+public class TlsProtocolVersion {
+    public static final TlsProtocolVersion SSLV3 = new TlsProtocolVersion(3, 0, "SSLv3");
+    public static final TlsProtocolVersion TLSv1_0 = new TlsProtocolVersion(3, 1, "TLSv1.0");
+    public static final TlsProtocolVersion TLSv1_1 = new TlsProtocolVersion(3, 2, "TLSv1.1");
+    public static final TlsProtocolVersion TLSv1_2 = new TlsProtocolVersion(3, 3, "TLSv1.2");
+
+    public final int major;
+    public final int minor;
+    public final String name;
+
+    private TlsProtocolVersion(int major, int minor, String name) {
+        this.major = major;
+        this.minor = minor;
+        this.name = name;
+    }
+
+    public static TlsProtocolVersion valueOf(int major, int minor) {
+        if (major == 3) {
+            switch (minor) {
+                case 0:
+                    return SSLV3;
+                case 1:
+                    return TLSv1_0;
+                case 2:
+                    return TLSv1_1;
+                case 3:
+                    return TLSv1_2;
+            }
+        }
+        return new TlsProtocolVersion(major, minor, major + "." + minor);
+    }
+
+    public static TlsProtocolVersion read(DataInput in) throws IOException {
+        int major = in.readUnsignedByte();
+        int minor = in.readUnsignedByte();
+        return TlsProtocolVersion.valueOf(major, minor);
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + major;
+        result = prime * result + minor;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TlsProtocolVersion other = (TlsProtocolVersion) obj;
+        if (major != other.major) {
+            return false;
+        }
+        if (minor != other.minor) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/support/src/test/java/tests/net/StuckServer.java b/support/src/test/java/tests/net/StuckServer.java
index f7a3118..d6b038b 100644
--- a/support/src/test/java/tests/net/StuckServer.java
+++ b/support/src/test/java/tests/net/StuckServer.java
@@ -21,6 +21,7 @@
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 
 /**
@@ -29,6 +30,19 @@
 public final class StuckServer {
     private static final boolean DEBUG = false;
 
+    // RFC 5737 implies this network -- "test net 1" -- will be unreachable.
+    // (There are two other networks to try if we have trouble with this one.)
+    // We've had trouble with 10.* in the past (because test labs running CTS often use
+    // net 10!) but hopefully this network will be better.
+    public static final InetAddress UNREACHABLE_ADDRESS;
+    static {
+        try {
+            UNREACHABLE_ADDRESS = InetAddress.getByAddress(new byte[] { (byte) 192, 0, 2, 0 });
+        } catch (UnknownHostException ex) {
+            throw new AssertionError(ex);
+        }
+    }
+
     private ServerSocket serverSocket;
     private InetSocketAddress address;
     private ArrayList<Socket> clients = new ArrayList<Socket>();
@@ -57,12 +71,7 @@
             }
         } else {
             // In general, though, you don't want to rely on listen(2) backlog. http://b/6971145.
-            // RFC 5737 implies this network will be unreachable. (There are two other networks
-            // to try if we have trouble with this one.)
-            // We've had trouble with 10.* in the past (because test labs running CTS often use
-            // net 10!) but hopefully this network will be better.
-            InetAddress testNet1 = InetAddress.getByAddress(new byte[] { (byte) 192, 0, 2, 0 });
-            this.address = new InetSocketAddress(testNet1, 80);
+            this.address = new InetSocketAddress(UNREACHABLE_ADDRESS, 80);
         }
     }
 
diff --git a/support/src/test/java/tests/resources/removed.jar b/support/src/test/java/tests/resources/removed.jar
new file mode 100644
index 0000000..2100322
--- /dev/null
+++ b/support/src/test/java/tests/resources/removed.jar
Binary files differ
diff --git a/support/src/test/java/tests/support/Support_Configuration.java b/support/src/test/java/tests/support/Support_Configuration.java
index 313a448..0856d44 100644
--- a/support/src/test/java/tests/support/Support_Configuration.java
+++ b/support/src/test/java/tests/support/Support_Configuration.java
@@ -32,390 +32,35 @@
  */
 public class Support_Configuration {
 
-    public static String DomainAddress = "apache.org";
+    public static final String DomainAddress = "apache.org";
 
-    public static String WebName = "jcltest.";
+    public static final String WebName = "jcltest.";
 
-    public static final String HomeAddress;
+    public static final String HomeAddress =  WebName + DomainAddress;
 
-    public static String TestResourcesDir = "/testres231";
+    public static final String TestResourcesDir = "/testres231";
 
-    public static final String TestResources;
+    public static final String TestResources =  HomeAddress + TestResourcesDir;
 
-    public static String HomeAddressResponse = "HTTP/1.1 200 OK";
+    public static final String HomeAddressResponse = "HTTP/1.1 200 OK";
 
-    public static String HomeAddressSoftware = "Jetty(6.0.x)";
+    public static final String HomeAddressSoftware = "Jetty(6.0.x)";
 
-    public static String SocksServerTestHost = "jcltest.apache.org";
+    public static final String SocksServerTestHost = "jcltest.apache.org";
 
-    public static int SocksServerTestPort = 1080;
-
-    // Need an IP address that does not resolve to a host name
-    public static String UnresolvedIP = "192.168.99.99";
+    public static final int SocksServerTestPort = 1080;
 
     // the bytes for an address which represents an address which is not
     // one of the addresses for any of our machines on which tests will run
     // it is used to verify we get the expected error when we try to bind
     // to an address that is not one of the machines local addresses
-    public static byte nonLocalAddressBytes[] = { 1, 0, 0, 0 };
+    public static final byte nonLocalAddressBytes[] = { 1, 0, 0, 0 };
 
-    public static String InetTestAddress = "localhost";
+    public static final String FTPTestAddress = "jcltest:jclpass@localhost";
 
-    public static String InetTestIP = "127.0.0.1";
+    public static final String URLConnectionLastModifiedString = "Mon, 14 Jun 1999 21:06:22 GMT";
 
-    // BEGIN android-added
-    public static byte[] InetTestAddr = {127, 0, 0, 1};
-    // END android-added
+    public static final long URLConnectionLastModified = 929394382000L;
 
-    public static String InetTestAddress2 = "localhost";
-
-    public static String InetTestIP2 = "127.0.0.1";
-
-    public static byte[] InetTestCaddr = { 9, 26, -56, -111 };
-
-    public static String IPv6GlobalAddressJcl4 = "2001:4860:8004::67"; // ipv6.google.com
-
-    // ip address that resolves to a host that is not present on the local
-    // network
-    // this allows us to check the timeouts for connect
-    public static String ResolvedNotExistingHost = "9.26.194.72";
-
-    // BEGIN android-changed
-    /**
-     * An address that resolves to more than one IP address so that the
-     * getAllByName test has something to test.
-     */
-    public static String SpecialInetTestAddress = "www.google.com";
-    // changed from jcltestmultiple.apache.org to www.google.com since
-    // the old address vaished from the net. www.google.com has also more
-    // than one addresses returned for this host name as needed by a test
-    // END android-changed
-
-    public static int SpecialInetTestAddressNumber = 4;
-
-    /**
-     * InetAlias1 and InetAlias2 must be different host names that resolve to
-     * the same IP address.
-     */
-    public static String InetAlias1 = "alias1.apache.org";
-
-    public static String InetAlias2 = "alias2.apache.org";
-
-    public static String FTPTestAddress = "jcltest:jclpass@localhost";
-
-    public static String URLConnectionLastModifiedString = "Mon, 14 Jun 1999 21:06:22 GMT";
-
-    public static long URLConnectionLastModified = 929394382000L;
-
-    public static long URLConnectionDate = 929106872000L;
-
-    static Hashtable<String, String> props = null;
-    static {
-        loadProperties();
-        HomeAddress = WebName + DomainAddress;
-        TestResources = HomeAddress + TestResourcesDir;
-    }
-
-    static void loadProperties() {
-        InputStream in = null;
-        Hashtable<String, String> props = new Hashtable<String, String>();
-
-        String iniName = System.getProperty("test.ini.file", "JCLAuto.ini");
-
-        try {
-            in = new FileInputStream(iniName);
-        } catch (IOException e) {
-        } catch (Exception e) {
-            System.out.println("SupportConfiguration.loadProperties()");
-            System.out.println(e);
-            e.printStackTrace();
-        }
-        if (in == null) {
-            try {
-                Class<?> cl = Class
-                        .forName("com.ibm.support.Support_Configuration");
-                in = cl.getResourceAsStream(iniName);
-            } catch (ClassNotFoundException e) {
-            }
-        }
-        try {
-            if (in != null) {
-                load(in, props);
-            }
-        } catch (IOException e) {
-        }
-        if (props.size() == 0) {
-            return;
-        }
-        String value;
-
-        value = props.get("DomainAddress");
-        if (value != null) {
-            DomainAddress = value;
-        }
-
-        value = props.get("WebName");
-        if (value != null) {
-            WebName = value;
-        }
-
-        value = props.get("TestResourcesDir");
-        if (value != null) {
-            TestResourcesDir = value;
-        }
-        value = props.get("HomeAddressResponse");
-        if (value != null) {
-            HomeAddressResponse = value;
-        }
-
-        value = props.get("HomeAddressSoftware");
-        if (value != null) {
-            HomeAddressSoftware = value;
-        }
-
-        value = props.get("SocksServerTestHost");
-        if (value != null) {
-            SocksServerTestHost = value;
-        }
-
-        value = props.get("SocksServerTestPort");
-        if (value != null) {
-            SocksServerTestPort = Integer.parseInt(value);
-        }
-
-        value = props.get("UnresolvedIP");
-        if (value != null) {
-            UnresolvedIP = value;
-        }
-
-        value = props.get("InetTestAddress");
-        if (value != null) {
-            InetTestAddress = value;
-        }
-
-        value = props.get("InetTestIP");
-        if (value != null) {
-            InetTestIP = value;
-            byte[] addr = new byte[4];
-            int last = 0;
-            try {
-                for (int i = 0; i < 3; i++) {
-                    int dot = InetTestIP.indexOf('.', last);
-                    addr[i] = (byte) Integer.parseInt(InetTestIP.substring(
-                            last, dot));
-                    last = dot + 1;
-                }
-                addr[3] = (byte) Integer.parseInt(InetTestIP.substring(last));
-                InetTestCaddr = addr;
-            } catch (RuntimeException e) {
-                System.out.println("Error parsing InetTestIP (" + InetTestIP
-                        + ")");
-                System.out.println(e);
-            }
-        }
-
-        value = props.get("NonLocalAddressBytes");
-        if (value != null) {
-            String nonLocalAddressBytesString = value;
-            byte[] addr = new byte[4];
-            int last = 0;
-            try {
-                for (int i = 0; i < 3; i++) {
-                    int dot = nonLocalAddressBytesString.indexOf('.', last);
-                    addr[i] = (byte) Integer
-                            .parseInt(nonLocalAddressBytesString.substring(
-                                    last, dot));
-                    last = dot + 1;
-                }
-                addr[3] = (byte) Integer.parseInt(nonLocalAddressBytesString
-                        .substring(last));
-                nonLocalAddressBytes = addr;
-            } catch (RuntimeException e) {
-                System.out.println("Error parsing NonLocalAddressBytes ("
-                        + nonLocalAddressBytesString + ")");
-                System.out.println(e);
-            }
-        }
-
-        value = props.get("InetTestAddress2");
-        if (value != null) {
-            InetTestAddress2 = value;
-        }
-
-        value = props.get("InetTestIP2");
-        if (value != null) {
-            InetTestIP2 = value;
-        }
-
-        value = props.get("SpecialInetTestAddress");
-        if (value != null) {
-            SpecialInetTestAddress = value;
-        }
-
-        value = props.get("SpecialInetTestAddressNumber");
-        if (value != null) {
-            SpecialInetTestAddressNumber = Integer.parseInt(value);
-        }
-
-        value = props.get("FTPTestAddress");
-        if (value != null) {
-            FTPTestAddress = value;
-        }
-
-        value = props.get("URLConnectionLastModifiedString");
-        if (value != null) {
-            URLConnectionLastModifiedString = value;
-        }
-
-        value = props.get("URLConnectionLastModified");
-        if (value != null) {
-            URLConnectionLastModified = Long.parseLong(value);
-        }
-
-        value = props.get("URLConnectionDate");
-        if (value != null) {
-            URLConnectionDate = Long.parseLong(value);
-        }
-
-        value = props.get("ResolvedNotExistingHost");
-        if (value != null) {
-            ResolvedNotExistingHost = value;
-        }
-
-        value = props.get("InetAlias1");
-        if (value != null) {
-            InetAlias1 = value;
-        }
-
-        value = props.get("InetAlias2");
-        if (value != null) {
-            InetAlias2 = value;
-        }
-
-        value = props.get("IPv6GlobalAddressJcl4");
-        if (value != null) {
-            IPv6GlobalAddressJcl4 = value;
-        }
-
-    }
-
-    static void load(InputStream in, Hashtable<String, String> result) throws IOException {
-        int NONE = 0, SLASH = 1, UNICODE = 2, CONTINUE = 3, DONE = 4, IGNORE = 5;
-        int mode = NONE, unicode = 0, count = 0, nextChar;
-        StringBuffer key = new StringBuffer(), value = new StringBuffer(), buffer = key;
-        boolean firstChar = true;
-
-        while ((nextChar = in.read()) != -1) {
-            if (mode == UNICODE) {
-                int digit = Character.digit((char) nextChar, 16);
-                if (digit >= 0) {
-                    unicode = (unicode << 4) + digit;
-                    if (++count < 4) {
-                        continue;
-                    }
-                }
-                mode = NONE;
-                buffer.append((char) unicode);
-                if (nextChar != '\n') {
-                    continue;
-                }
-            }
-            if (mode == SLASH) {
-                mode = NONE;
-                switch (nextChar) {
-                case '\r':
-                    mode = CONTINUE; // Look for a following \n
-                    continue;
-                case '\n':
-                    mode = IGNORE; // Ignore whitespace on the next line
-                    continue;
-                case 'b':
-                    nextChar = '\b';
-                    break;
-                case 'f':
-                    nextChar = '\f';
-                    break;
-                case 'n':
-                    nextChar = '\n';
-                    break;
-                case 'r':
-                    nextChar = '\r';
-                    break;
-                case 't':
-                    nextChar = '\t';
-                    break;
-                case 'u':
-                    mode = UNICODE;
-                    unicode = count = 0;
-                    continue;
-                }
-            } else {
-                switch (nextChar) {
-                case '#':
-                case '!':
-                    if (firstChar) {
-                        while ((nextChar = in.read()) != -1) {
-                            if (nextChar == '\r' || nextChar == '\n') {
-                                break;
-                            }
-                        }
-                        continue;
-                    }
-                    break;
-                case '\n':
-                    if (mode == CONTINUE) { // Part of a \r\n sequence
-                        mode = IGNORE; // Ignore whitespace on the next line
-                        continue;
-                    }
-                    // fall into the next case
-                case '\r':
-                    mode = NONE;
-                    firstChar = true;
-                    if (key.length() > 0 || buffer == value) {
-                        result.put(key.toString(), value.toString());
-                    }
-                    key.setLength(0);
-                    value.setLength(0);
-                    buffer = key;
-                    continue;
-                case '\\':
-                    mode = SLASH;
-                    continue;
-                case ':':
-                case '=':
-                    if (buffer == key) {
-                        buffer = value;
-                        continue;
-                    }
-                    break;
-                }
-                char c = (char) nextChar;
-                if ((c >= 0x1c && c <= 0x20) || (c >= 0x9 && c <= 0xd)) {
-                    if (mode == CONTINUE) {
-                        mode = IGNORE;
-                    }
-                    if (buffer.length() == 0 || mode == IGNORE) {
-                        continue;
-                    }
-                    if (buffer == key) {
-                        mode = DONE;
-                        continue;
-                    }
-                }
-                if (mode == IGNORE || mode == CONTINUE) {
-                    mode = NONE;
-                }
-            }
-            firstChar = false;
-            if (mode == DONE) {
-                buffer = value;
-                mode = NONE;
-            }
-            buffer.append((char) nextChar);
-        }
-        if (key.length() > 0 || buffer == value) {
-            result.put(key.toString(), value.toString());
-        }
-    }
-
+    public static final long URLConnectionDate = 929106872000L;
 }
diff --git a/support/src/test/java/tests/util/DelegatingSSLSocketFactory.java b/support/src/test/java/tests/util/DelegatingSSLSocketFactory.java
new file mode 100644
index 0000000..5513210
--- /dev/null
+++ b/support/src/test/java/tests/util/DelegatingSSLSocketFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tests.util;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * {@link SSLSocketFactory} which delegates all invocations to the provided delegate
+ * {@code SSLSocketFactory}.
+ */
+public class DelegatingSSLSocketFactory extends SSLSocketFactory {
+
+  private final SSLSocketFactory mDelegate;
+
+  public DelegatingSSLSocketFactory(SSLSocketFactory delegate) {
+    this.mDelegate = delegate;
+  }
+
+  /**
+   * Invoked after obtaining a socket from the delegate and before returning it to the caller.
+   *
+   * <p>The default implementation does nothing.
+   */
+  protected void configureSocket(@SuppressWarnings("unused") SSLSocket socket) {}
+
+  @Override
+  public String[] getDefaultCipherSuites() {
+    return mDelegate.getDefaultCipherSuites();
+  }
+
+  @Override
+  public String[] getSupportedCipherSuites() {
+    return mDelegate.getSupportedCipherSuites();
+  }
+
+  @Override
+  public Socket createSocket() throws IOException {
+    SSLSocket socket = (SSLSocket) mDelegate.createSocket();
+    configureSocket(socket);
+    return socket;
+  }
+
+  @Override
+  public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+      throws IOException {
+    SSLSocket socket = (SSLSocket) mDelegate.createSocket(s, host, port, autoClose);
+    configureSocket(socket);
+    return socket;
+  }
+
+  @Override
+  public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+      SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port);
+      configureSocket(socket);
+      return socket;
+  }
+
+  @Override
+  public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+      throws IOException, UnknownHostException {
+    SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port, localHost, localPort);
+    configureSocket(socket);
+    return socket;
+  }
+
+  @Override
+  public Socket createSocket(InetAddress host, int port) throws IOException {
+    SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port);
+    configureSocket(socket);
+    return socket;
+  }
+
+  @Override
+  public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+    int localPort) throws IOException {
+    SSLSocket socket =
+        (SSLSocket) mDelegate.createSocket(address, port, localAddress, localPort);
+    configureSocket(socket);
+    return socket;
+  }
+}
diff --git a/support/src/test/java/tests/util/ForEachRunner.java b/support/src/test/java/tests/util/ForEachRunner.java
new file mode 100644
index 0000000..2c222b2
--- /dev/null
+++ b/support/src/test/java/tests/util/ForEachRunner.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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 tests.util;
+
+/**
+ * Runner which executes the provided code under test (via a callback) for each provided input
+ * value.
+ */
+public final class ForEachRunner {
+
+  /**
+   * Callback parameterized with a value.
+   */
+  public interface Callback<T> {
+    /**
+     * Invokes the callback for the provided value.
+     */
+    void run(T value) throws Exception;
+  }
+
+  private ForEachRunner() {}
+
+  /**
+   * Invokes the provided callback for each of the provided named values.
+   *
+   * @param namesAndValues named values represented as name-value pairs.
+   *
+   * @param <T> type of value.
+   */
+  public static <T> void runNamed(Callback<T> callback, Iterable<Pair<String, T>> namesAndValues)
+      throws Exception {
+    for (Pair<String, T> nameAndValue : namesAndValues) {
+      try {
+        callback.run(nameAndValue.getSecond());
+      } catch (Throwable e) {
+        throw new Exception("Failed for " + nameAndValue.getFirst() + ": " + e.getMessage(), e);
+      }
+    }
+  }
+}
diff --git a/support/src/test/java/tests/util/Pair.java b/support/src/test/java/tests/util/Pair.java
new file mode 100644
index 0000000..9b0906d
--- /dev/null
+++ b/support/src/test/java/tests/util/Pair.java
@@ -0,0 +1,107 @@
+/*
+ * 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 tests.util;
+
+/**
+ * Pair of typed values.
+ *
+ * <p>Pairs are obtained using {@link #of(Object, Object) of}.
+ *
+ * @param <F> type of the first value.
+ * @param <S> type of the second value.
+ */
+public class Pair<F, S> {
+  private final F mFirst;
+  private final S mSecond;
+
+  private Pair(F first, S second) {
+    mFirst = first;
+    mSecond = second;
+  }
+
+  /**
+   * Gets the pair consisting of the two provided values.
+   *
+   * @param first first value or {@code null}.
+   * @param second second value or {@code null}.
+   */
+  public static <F, S> Pair<F, S> of(F first, S second) {
+    return new Pair<F, S>(first, second);
+  }
+
+  /**
+   * Gets the first value from this pair.
+   *
+   * @return value or {@code null}.
+   */
+  public F getFirst() {
+    return mFirst;
+  }
+
+  /**
+   * Gets the second value from this pair.
+   *
+   * @return value or {@code null}.
+   */
+  public S getSecond() {
+    return mSecond;
+  }
+
+  @Override
+  public String toString() {
+    return "Pair[" + mFirst + ", " + mSecond + "]";
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode());
+    result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    @SuppressWarnings("rawtypes")
+    Pair other = (Pair) obj;
+    if (mFirst == null) {
+      if (other.mFirst != null) {
+        return false;
+      }
+    } else if (!mFirst.equals(other.mFirst)) {
+      return false;
+    }
+    if (mSecond == null) {
+      if (other.mSecond != null) {
+        return false;
+      }
+    } else if (!mSecond.equals(other.mSecond)) {
+      return false;
+    }
+    return true;
+  }
+}
diff --git a/tzdata/Android.mk b/tzdata/Android.mk
new file mode 100644
index 0000000..9da8832
--- /dev/null
+++ b/tzdata/Android.mk
@@ -0,0 +1,51 @@
+# Copyright (C) 2015 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Subprojects with separate makefiles
+subdirs := update_test_app
+subdir_makefiles := $(call all-named-subdir-makefiles,$(subdirs))
+
+# Library of tools classes for tzdata updates. Not required on device, except in tests.
+include $(CLEAR_VARS)
+LOCAL_MODULE := tzdata_tools
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under, tools/src/main)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Library of support classes for tzdata updates. Shared between update generation and
+# on-device code.
+include $(CLEAR_VARS)
+LOCAL_MODULE := tzdata_update
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under, update/src/main)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Tests for tzdata_update code
+include $(CLEAR_VARS)
+LOCAL_MODULE := tzdata_update-tests
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under, update/src/test)
+LOCAL_JAVACFLAGS := -encoding UTF-8
+LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update tzdata_tools
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(subdir_makefiles)
diff --git a/tzdata/tools/createIcuUpdateResources.sh b/tzdata/tools/createIcuUpdateResources.sh
new file mode 100755
index 0000000..2db7132
--- /dev/null
+++ b/tzdata/tools/createIcuUpdateResources.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+#
+# A script that generates an ICU data file containing just timezone rules data.
+# The file can be used to provide time zone rules updates for compatible
+# devices. Note: Only the rules are contained and new timezones will not have
+# the translations.
+#
+# Usage:
+# ./createIcuUpdateResources.sh <tzdata tar.gz file> <ICU version>
+#
+# e.g.
+# ./createIcuUpdateResources.sh ~/Downloads/tzdata2015b.tar.gz 55
+#
+# After execution the file is generated.
+
+if (( $# != 2 )); then
+  echo "Missing arguments"
+  echo "Usage:"
+  echo "./createIcuUpdateResources.sh <tzdata tar.gz file> <ICU version>"
+  exit 1
+fi
+
+if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+  echo "Configure your environment with build/envsetup.sh and lunch"
+  exit 1
+fi
+
+TZ_DATA_FILE=$1
+ICU_VERSION=$2
+
+if [[ ! -f ${TZ_DATA_FILE} ]]; then
+  echo "${TZ_DATA_FILE} not found"
+  exit 1
+fi
+
+# Keep track of the original working dir. Must be the "tools" dir.
+START_DIR=`pwd`
+ICU_DIR=${ANDROID_BUILD_TOP}/external/icu/icu4c/source
+BUILD_DIR=${START_DIR}/icu_build
+
+# Fail if anything below fails
+set -e
+
+rm -rf ${BUILD_DIR}
+mkdir -p ${BUILD_DIR}
+cd ${BUILD_DIR}
+
+# Configure the build
+${ICU_DIR}/runConfigureICU Linux
+mkdir -p ${BUILD_DIR}/bin
+cd ${BUILD_DIR}/tools/tzcode
+ln -s ${ICU_DIR}/tools/tzcode/icuregions ./icuregions
+ln -s ${ICU_DIR}/tools/tzcode/icuzones ./icuzones
+cp ${TZ_DATA_FILE} .
+
+# Make the tools
+make
+
+# Then make the whole thing
+cd ${BUILD_DIR}
+make -j32
+
+# Generate the tzdata.lst file used to configure which files are included.
+ICU_LIB_DIR=${BUILD_DIR}/lib
+BIN_DIR=${BUILD_DIR}/bin
+TZ_FILES=tzdata.lst
+
+echo metaZones.res > ${TZ_FILES}
+echo timezoneTypes.res >> ${TZ_FILES}
+echo windowsZones.res >> ${TZ_FILES}
+echo zoneinfo64.res >> ${TZ_FILES}
+
+# Copy all the .res files we need here a from, e.g. ./data/out/build/icudt55l
+RES_DIR=data/out/build/icudt${ICU_VERSION}l
+cp ${RES_DIR}/metaZones.res ${BUILD_DIR}
+cp ${RES_DIR}/timezoneTypes.res ${BUILD_DIR}
+cp ${RES_DIR}/windowsZones.res ${BUILD_DIR}
+cp ${RES_DIR}/zoneinfo64.res ${BUILD_DIR}
+
+# This is the package name required for the .dat file to be accepted by ICU.
+# This also affects the generated file name.
+ICU_PACKAGE=icudt${ICU_VERSION}l
+
+# Create the file
+LD_LIBRARY_PATH=${ICU_LIB_DIR} ${BIN_DIR}/pkgdata -F -m common -v -T . -d . -p ${ICU_PACKAGE} ${TZ_FILES}
+cp ${ICU_PACKAGE}.dat ${START_DIR}/icu_tzdata.dat
+
+# Copy the file to the original working dir.
+echo File can be found here: ${START_DIR}/icu_tzdata.dat
diff --git a/tzdata/tools/createTzDataBundle.sh b/tzdata/tools/createTzDataBundle.sh
new file mode 100755
index 0000000..05646fc
--- /dev/null
+++ b/tzdata/tools/createTzDataBundle.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# A script to generate TZ data updates.
+#
+# Usage: ./createTzDataBundle.sh <tzupdate.properties file> <output file>
+# See libcore.tzdata.update.tools.CreateTzDataBundle for more information.
+
+TOOLS_DIR=src/main/libcore/tzdata/update/tools
+UPDATE_DIR=../update/src/main/libcore/tzdata/update
+GEN_DIR=./gen
+
+# Fail if anything below fails
+set -e
+
+rm -rf ${GEN_DIR}
+mkdir -p ${GEN_DIR}
+
+javac \
+    ${TOOLS_DIR}/CreateTzDataBundle.java \
+    ${TOOLS_DIR}/TzDataBundleBuilder.java \
+    ${UPDATE_DIR}/ConfigBundle.java \
+    ${UPDATE_DIR}/FileUtils.java \
+    -d ${GEN_DIR}
+
+java -cp ${GEN_DIR} libcore.tzdata.update.tools.CreateTzDataBundle $@
diff --git a/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java b/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java
new file mode 100644
index 0000000..cdb004a
--- /dev/null
+++ b/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update.tools;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.util.Properties;
+import libcore.tzdata.update.ConfigBundle;
+import libcore.tzdata.update.FileUtils;
+
+/**
+ * A command-line tool for creating a TZ data update bundle.
+ *
+ * Args:
+ * tzdata.properties file - the file describing the bundle (see template file in tzdata/tools)
+ * output file - the name of the file to be generated
+ */
+public class CreateTzDataBundle {
+
+    private CreateTzDataBundle() {}
+
+    public static void main(String[] args) throws Exception {
+        if (args.length != 2) {
+            printUsage();
+            System.exit(1);
+        }
+        File f = new File(args[0]);
+        if (!f.exists()) {
+            System.err.println("Properties file " + f + " not found");
+            printUsage();
+            System.exit(2);
+        }
+        Properties p = loadProperties(f);
+        TzDataBundleBuilder builder = new TzDataBundleBuilder()
+                .setTzDataVersion(getMandatoryProperty(p, "tzdata.version"))
+                .addBionicTzData(getMandatoryPropertyFile(p, "bionic.file"))
+                .addIcuTzData(getMandatoryPropertyFile(p, "icu.file"));
+
+        int i = 1;
+        while (true) {
+            String localFileNameProperty = "checksum.file.local." + i;
+            String localFileName = p.getProperty(localFileNameProperty);
+            String onDeviceFileNameProperty = "checksum.file.ondevice." + i;
+            String onDeviceFileName = p.getProperty(onDeviceFileNameProperty);
+            boolean foundLocalFileNameProperty = localFileName != null;
+            boolean foundOnDeviceFileNameProperty = onDeviceFileName != null;
+            if (!foundLocalFileNameProperty && !foundOnDeviceFileNameProperty) {
+                break;
+            } else if (foundLocalFileNameProperty != foundOnDeviceFileNameProperty) {
+                System.out.println("Properties file must specify both, or neither of: "
+                        + localFileNameProperty + " and " + onDeviceFileNameProperty);
+                System.exit(5);
+            }
+
+            long checksum = FileUtils.calculateChecksum(new File(localFileName));
+            builder.addChecksum(onDeviceFileName, checksum);
+            i++;
+        }
+        if (i == 1) {
+            // For safety we enforce >= 1 checksum entry. The installer does not require it.
+            System.out.println("There must be at least one checksum file");
+            System.exit(6);
+        }
+        System.out.println("Update contains checksums for " + (i-1) + " files");
+
+        ConfigBundle bundle = builder.build();
+        File outputFile = new File(args[1]);
+        try (OutputStream os = new FileOutputStream(outputFile)) {
+            os.write(bundle.getBundleBytes());
+        }
+        System.out.println("Wrote: " + outputFile);
+    }
+
+    private static File getMandatoryPropertyFile(Properties p, String propertyName) {
+        String fileName = getMandatoryProperty(p, propertyName);
+        File file = new File(fileName);
+        if (!file.exists()) {
+            System.out.println(
+                    "Missing file: " + file + " for property " + propertyName + " does not exist.");
+            printUsage();
+            System.exit(4);
+        }
+        return file;
+    }
+
+    private static String getMandatoryProperty(Properties p, String propertyName) {
+        String value = p.getProperty(propertyName);
+        if (value == null) {
+            System.out.println("Missing property: " + propertyName);
+            printUsage();
+            System.exit(3);
+        }
+        return value;
+    }
+
+    private static Properties loadProperties(File f) throws IOException {
+        Properties p = new Properties();
+        try (Reader reader = new InputStreamReader(new FileInputStream(f))) {
+            p.load(reader);
+        }
+        return p;
+    }
+
+    private static void printUsage() {
+        System.out.println("Usage:");
+        System.out.println("\t" + CreateTzDataBundle.class.getName() +
+                " <tzupdate.properties file> <output file>");
+    }
+}
diff --git a/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java b/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java
new file mode 100644
index 0000000..3550c6f
--- /dev/null
+++ b/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update.tools;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import libcore.tzdata.update.ConfigBundle;
+
+/**
+ * A class for creating a {@link ConfigBundle} containing timezone update data.
+ */
+public final class TzDataBundleBuilder {
+
+    private String tzDataVersion;
+    private StringBuilder checksumsFileContent = new StringBuilder();
+    private File zoneInfoFile;
+    private File icuTzDataFile;
+
+    public TzDataBundleBuilder setTzDataVersion(String tzDataVersion) {
+        this.tzDataVersion = tzDataVersion;
+        return this;
+    }
+
+    public TzDataBundleBuilder addChecksum(String fileName, long checksum) {
+        checksumsFileContent.append(Long.toString(checksum))
+                .append(',')
+                .append(fileName)
+                .append('\n');
+        return this;
+    }
+
+    public TzDataBundleBuilder addBionicTzData(File zoneInfoFile) {
+        this.zoneInfoFile = zoneInfoFile;
+        return this;
+    }
+
+    public TzDataBundleBuilder addIcuTzData(File icuTzDataFile) {
+        this.icuTzDataFile = icuTzDataFile;
+        return this;
+    }
+
+    /**
+     * Builds a {@link libcore.tzdata.update.ConfigBundle}.
+     */
+    public ConfigBundle build() throws IOException {
+        if (tzDataVersion == null) {
+            throw new IllegalStateException("Missing tzDataVersion");
+        }
+        if (zoneInfoFile == null) {
+            throw new IllegalStateException("Missing zoneInfo file");
+        }
+
+        return buildUnvalidated();
+    }
+
+    // For use in tests.
+    public TzDataBundleBuilder clearChecksumEntries() {
+        checksumsFileContent.setLength(0);
+        return this;
+    }
+
+    // For use in tests.
+    public TzDataBundleBuilder clearBionicTzData() {
+        this.zoneInfoFile = null;
+        return this;
+    }
+
+    /**
+     * For use in tests. Use {@link #build()}.
+     */
+    public ConfigBundle buildUnvalidated() throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ZipOutputStream zos = new ZipOutputStream(baos)) {
+            addZipEntry(zos, ConfigBundle.CHECKSUMS_FILE_NAME,
+                    checksumsFileContent.toString().getBytes(StandardCharsets.UTF_8));
+            if (tzDataVersion != null) {
+                addZipEntry(zos, ConfigBundle.TZ_DATA_VERSION_FILE_NAME,
+                        tzDataVersion.getBytes(StandardCharsets.UTF_8));
+            }
+            if (zoneInfoFile != null) {
+                addZipEntry(zos, ConfigBundle.ZONEINFO_FILE_NAME,
+                        readFileAsByteArray(zoneInfoFile));
+            }
+            if (icuTzDataFile != null) {
+                addZipEntry(zos, ConfigBundle.ICU_DATA_FILE_NAME,
+                        readFileAsByteArray(icuTzDataFile));
+            }
+        }
+        return new ConfigBundle(baos.toByteArray());
+    }
+
+    private static void addZipEntry(ZipOutputStream zos, String name, byte[] content)
+            throws IOException {
+        ZipEntry zipEntry = new ZipEntry(name);
+        zipEntry.setSize(content.length);
+        zos.putNextEntry(zipEntry);
+        zos.write(content);
+        zos.closeEntry();
+    }
+
+    /**
+     * Returns the contents of 'path' as a byte array.
+     */
+    public static byte[] readFileAsByteArray(File file) throws IOException {
+        byte[] buffer = new byte[8192];
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (FileInputStream  fis = new FileInputStream(file)) {
+            int count;
+            while ((count = fis.read(buffer)) != -1) {
+                baos.write(buffer, 0, count);
+            }
+        }
+        return baos.toByteArray();
+    }
+}
+
diff --git a/tzdata/tools/tzupdate.properties b/tzdata/tools/tzupdate.properties
new file mode 100644
index 0000000..e3fe002
--- /dev/null
+++ b/tzdata/tools/tzupdate.properties
@@ -0,0 +1,14 @@
+# Edit these to reflect the update files.
+
+# This should be the tzdata version. e.g. "2015a". Lexicographical sort order
+# may become important in future so if inventing interim releases only add
+# characters to the end.
+tzdata.version=
+bionic.file=
+icu.file=
+
+# Edit these as required to point to the file expected to exist on the device.
+checksum.file.local.1=../../../bionic/libc/zoneinfo/tzdata
+checksum.file.ondevice.1=/system/usr/share/zoneinfo/tzdata
+checksum.file.local.2=../../../external/icu/icu4c/source/stubdata/icudt55l.dat
+checksum.file.ondevice.2=/system/usr/icu/icudt55l.dat
diff --git a/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java b/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java
new file mode 100644
index 0000000..6e2ff9d
--- /dev/null
+++ b/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * A configuration bundle. This is a thin wrapper around some in-memory bytes representing a zip
+ * archive and logic for its safe extraction.
+ */
+public final class ConfigBundle {
+
+    /** The name of the file inside the bundle containing the TZ data version. */
+    public static final String TZ_DATA_VERSION_FILE_NAME = "tzdata_version";
+
+    /** The name of the file inside the bundle containing the expected device checksums. */
+    public static final String CHECKSUMS_FILE_NAME = "checksums";
+
+    /** The name of the file inside the bundle containing bionic/libcore TZ data. */
+    public static final String ZONEINFO_FILE_NAME = "tzdata";
+
+    /** The name of the file inside the bundle containing ICU TZ data. */
+    public static final String ICU_DATA_FILE_NAME = "icu/icu_tzdata.dat";
+
+    private static final int BUFFER_SIZE = 8192;
+
+    private final byte[] bytes;
+
+    public ConfigBundle(byte[] bytes) {
+        this.bytes = bytes;
+    }
+
+    public byte[] getBundleBytes() {
+        return bytes;
+    }
+
+    public void extractTo(File targetDir) throws IOException {
+        extractZipSafely(new ByteArrayInputStream(bytes), targetDir, true /* makeWorldReadable */);
+    }
+
+    /** Visible for testing */
+    static void extractZipSafely(InputStream is, File targetDir, boolean makeWorldReadable)
+            throws IOException {
+
+        // Create the extraction dir, if needed.
+        FileUtils.ensureDirectoriesExist(targetDir, makeWorldReadable);
+
+        try (ZipInputStream zipInputStream = new ZipInputStream(is)) {
+            byte[] buffer = new byte[BUFFER_SIZE];
+            ZipEntry entry;
+            while ((entry = zipInputStream.getNextEntry()) != null) {
+                // Validate the entry name: make sure the unpacked file will exist beneath the
+                // targetDir.
+                String name = entry.getName();
+                File entryFile = FileUtils.createSubFile(targetDir, name);
+
+                if (entry.isDirectory()) {
+                    FileUtils.ensureDirectoriesExist(entryFile, makeWorldReadable);
+                } else {
+                    // Create the path if there was no directory entry.
+                    if (!entryFile.getParentFile().exists()) {
+                        FileUtils.ensureDirectoriesExist(
+                                entryFile.getParentFile(), makeWorldReadable);
+                    }
+
+                    try (FileOutputStream fos = new FileOutputStream(entryFile)) {
+                        int count;
+                        while ((count = zipInputStream.read(buffer)) != -1) {
+                            fos.write(buffer, 0, count);
+                        }
+                        // sync to disk
+                        fos.getFD().sync();
+                    }
+                    // mark entryFile -rw-r--r--
+                    if (makeWorldReadable) {
+                        FileUtils.makeWorldReadable(entryFile);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ConfigBundle that = (ConfigBundle) o;
+
+        if (!Arrays.equals(bytes, that.bytes)) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/tzdata/update/src/main/libcore/tzdata/update/FileUtils.java b/tzdata/update/src/main/libcore/tzdata/update/FileUtils.java
new file mode 100644
index 0000000..8b7da78
--- /dev/null
+++ b/tzdata/update/src/main/libcore/tzdata/update/FileUtils.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.zip.CRC32;
+
+/**
+ * Utility methods for files operations.
+ */
+public final class FileUtils {
+
+    private FileUtils() {
+    }
+
+    /**
+     * Creates a new {@link java.io.File} from the {@code parentDir} and {@code name}, but only if
+     * the
+     * resulting file would exist beneath {@code parentDir}. Useful if {@code name} could contain
+     * "/../" or symlinks. The returned object has an absolute path.
+     *
+     * @throws java.io.IOException
+     *         if the file would not exist beneath {@code parentDir}
+     */
+    public static File createSubFile(File parentDir, String name) throws IOException {
+        // The subFile must exist beneath parentDir. If name contains "/../" this may not be the
+        // case so we check.
+        File subFile = canonicalizeDirPath(new File(parentDir, name));
+        if (!subFile.getPath().startsWith(parentDir.getCanonicalPath())) {
+            throw new IOException(name + " must exist beneath " + parentDir);
+        }
+        return subFile;
+    }
+
+    /**
+     * Makes sure a directory exists. If it doesn't exist, it is created. Parent directories are
+     * also created as needed. If {@code makeWorldReadable} is {@code true} the directory's default
+     * permissions will be set. Even when {@code makeWorldReadable} is {@code true}, only
+     * directories explicitly created will have their permissions set; existing directories are
+     * untouched.
+     *
+     * @throws IOException
+     *         if the directory or one of its parents did not already exist and could not be created
+     */
+    public static void ensureDirectoriesExist(File dir, boolean makeWorldReadable)
+            throws IOException {
+        LinkedList<File> dirs = new LinkedList<>();
+        File currentDir = dir;
+        do {
+            dirs.addFirst(currentDir);
+            currentDir = currentDir.getParentFile();
+        } while (currentDir != null);
+
+        for (File dirToCheck : dirs) {
+            if (!dirToCheck.exists()) {
+                if (!dirToCheck.mkdir()) {
+                    throw new IOException("Unable to create directory: " + dir);
+                }
+                if (makeWorldReadable) {
+                    makeDirectoryWorldAccessible(dirToCheck);
+                }
+            } else if (!dirToCheck.isDirectory()) {
+                throw new IOException(dirToCheck + " exists but is not a directory");
+            }
+        }
+    }
+
+    /**
+     * Returns a file with all symlinks and relative paths such as "/../" resolved <em>except</em>
+     * for the base name (the last element of the path). Useful for detecting symlinks.
+     */
+    public static File canonicalizeDirPath(File file) throws IOException {
+        return new File(file.getParentFile().getCanonicalFile(), file.getName());
+    }
+
+    public static void makeDirectoryWorldAccessible(File directory) throws IOException {
+        if (!directory.isDirectory()) {
+            throw new IOException(directory + " must be a directory");
+        }
+        makeWorldReadable(directory);
+        if (!directory.setExecutable(true, false /* ownerOnly */)) {
+            throw new IOException("Unable to make " + directory + " world-executable");
+        }
+    }
+
+    public static void makeWorldReadable(File file) throws IOException {
+        if (!file.setReadable(true, false /* ownerOnly */)) {
+            throw new IOException("Unable to make " + file + " world-readable");
+        }
+    }
+
+    /**
+     * Calculates the checksum from the contents of a file.
+     */
+    public static long calculateChecksum(File file) throws IOException {
+        final int BUFFER_SIZE = 8196;
+        CRC32 crc32 = new CRC32();
+        try (FileInputStream fis = new FileInputStream(file)) {
+            byte[] buffer = new byte[BUFFER_SIZE];
+            int count;
+            while ((count = fis.read(buffer)) != -1) {
+                crc32.update(buffer, 0, count);
+            }
+        }
+        return crc32.getValue();
+    }
+
+    public static void rename(File from, File to) throws IOException {
+        ensureFileDoesNotExist(to);
+        if (!from.renameTo(to)) {
+            throw new IOException("Unable to rename " + from + " to " + to);
+        }
+    }
+
+    public static void ensureFileDoesNotExist(File file) throws IOException {
+        if (file.exists()) {
+            if (!file.isFile()) {
+                throw new IOException(file + " is not a file");
+            }
+            doDelete(file);
+        }
+    }
+
+    public static void doDelete(File file) throws IOException {
+        if (!file.delete()) {
+            throw new IOException("Unable to delete: " + file);
+        }
+    }
+
+    public static boolean isSymlink(File file) throws IOException {
+        return !file.getCanonicalPath().equals(canonicalizeDirPath(file).getPath());
+    }
+
+    public static void deleteRecursive(File toDelete) throws IOException {
+        if (toDelete.isDirectory()) {
+            for (File file : toDelete.listFiles()) {
+                if (file.isDirectory() && !FileUtils.isSymlink(file)) {
+                    // The isSymlink() check is important so that we don't delete files in other
+                    // directories: only the symlink itself.
+                    deleteRecursive(file);
+                } else {
+                    // Delete symlinks to directories or files.
+                    FileUtils.doDelete(file);
+                }
+            }
+            String[] remainingFiles = toDelete.list();
+            if (remainingFiles.length != 0) {
+                throw new IOException("Unable to delete files: " + Arrays
+                        .toString(remainingFiles));
+            }
+        }
+        FileUtils.doDelete(toDelete);
+    }
+
+    public static boolean filesExist(File rootDir, String... fileNames) throws IOException {
+        for (String fileName : fileNames) {
+            File file = new File(rootDir, fileName);
+            if (!file.exists()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Read all lines from a UTF-8 encoded file, returning them as a list of strings.
+     */
+    public static List<String> readLines(File file) throws IOException {
+        FileInputStream in = new FileInputStream(file);
+        try (BufferedReader fileReader = new BufferedReader(
+                new InputStreamReader(in, StandardCharsets.UTF_8));
+        ) {
+            List<String> lines = new ArrayList<>();
+            String line;
+            while ((line = fileReader.readLine()) != null) {
+                lines.add(line);
+            }
+            return lines;
+        }
+    }
+}
diff --git a/tzdata/update/src/main/libcore/tzdata/update/TzDataBundleInstaller.java b/tzdata/update/src/main/libcore/tzdata/update/TzDataBundleInstaller.java
new file mode 100644
index 0000000..df0b2a7
--- /dev/null
+++ b/tzdata/update/src/main/libcore/tzdata/update/TzDataBundleInstaller.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A bundle-validation / extraction class. Separate from the services code that uses it for easier
+ * testing.
+ */
+public final class TzDataBundleInstaller {
+
+    static final String CURRENT_TZ_DATA_DIR_NAME = "current";
+    static final String WORKING_DIR_NAME = "working";
+    static final String OLD_TZ_DATA_DIR_NAME = "old";
+
+    private final String logTag;
+    private final File installDir;
+
+    public TzDataBundleInstaller(String logTag, File installDir) {
+        this.logTag = logTag;
+        this.installDir = installDir;
+    }
+
+    /**
+     * Install the supplied content.
+     *
+     * <p>Errors during unpacking or installation will throw an {@link IOException}.
+     * If the content is invalid this method returns {@code false}.
+     * If the installation completed successfully this method returns {@code true}.
+     */
+    public boolean install(byte[] content) throws IOException {
+        File oldTzDataDir = new File(installDir, OLD_TZ_DATA_DIR_NAME);
+        if (oldTzDataDir.exists()) {
+            FileUtils.deleteRecursive(oldTzDataDir);
+        }
+
+        File currentTzDataDir = new File(installDir, CURRENT_TZ_DATA_DIR_NAME);
+        File workingDir = new File(installDir, WORKING_DIR_NAME);
+
+        Slog.i(logTag, "Applying time zone update");
+        File unpackedContentDir = unpackBundle(content, workingDir);
+        try {
+            if (!checkBundleFilesExist(unpackedContentDir)) {
+                Slog.i(logTag, "Update not applied: Bundle is missing files");
+                return false;
+            }
+
+            if (verifySystemChecksums(unpackedContentDir)) {
+                FileUtils.makeDirectoryWorldAccessible(unpackedContentDir);
+
+                if (currentTzDataDir.exists()) {
+                    Slog.i(logTag, "Moving " + currentTzDataDir + " to " + oldTzDataDir);
+                    FileUtils.rename(currentTzDataDir, oldTzDataDir);
+                }
+                Slog.i(logTag, "Moving " + unpackedContentDir + " to " + currentTzDataDir);
+                FileUtils.rename(unpackedContentDir, currentTzDataDir);
+                Slog.i(logTag, "Update applied: " + currentTzDataDir + " successfully created");
+                return true;
+            }
+            Slog.i(logTag, "Update not applied: System checksum did not match");
+            return false;
+        } finally {
+            deleteBestEffort(oldTzDataDir);
+            deleteBestEffort(unpackedContentDir);
+        }
+    }
+
+    private void deleteBestEffort(File dir) {
+        if (dir.exists()) {
+            try {
+                FileUtils.deleteRecursive(dir);
+            } catch (IOException e) {
+                // Logged but otherwise ignored.
+                Slog.w(logTag, "Unable to delete " + dir, e);
+            }
+        }
+    }
+
+    private File unpackBundle(byte[] content, File targetDir) throws IOException {
+        Slog.i(logTag, "Unpacking update content to: " + targetDir);
+        ConfigBundle bundle = new ConfigBundle(content);
+        bundle.extractTo(targetDir);
+        return targetDir;
+    }
+
+    private boolean checkBundleFilesExist(File unpackedContentDir) throws IOException {
+        Slog.i(logTag, "Verifying bundle contents");
+        return FileUtils.filesExist(unpackedContentDir,
+                ConfigBundle.TZ_DATA_VERSION_FILE_NAME,
+                ConfigBundle.CHECKSUMS_FILE_NAME,
+                ConfigBundle.ZONEINFO_FILE_NAME,
+                ConfigBundle.ICU_DATA_FILE_NAME);
+    }
+
+    private boolean verifySystemChecksums(File unpackedContentDir) throws IOException {
+        Slog.i(logTag, "Verifying system file checksums");
+        File checksumsFile = new File(unpackedContentDir, ConfigBundle.CHECKSUMS_FILE_NAME);
+        for (String line : FileUtils.readLines(checksumsFile)) {
+            int delimiterPos = line.indexOf(',');
+            if (delimiterPos <= 0 || delimiterPos == line.length() - 1) {
+                throw new IOException("Bad checksum entry: " + line);
+            }
+            long expectedChecksum;
+            try {
+                expectedChecksum = Long.parseLong(line.substring(0, delimiterPos));
+            } catch (NumberFormatException e) {
+                throw new IOException("Invalid checksum value: " + line);
+            }
+            String filePath = line.substring(delimiterPos + 1);
+            File file = new File(filePath);
+            if (!file.exists()) {
+                Slog.i(logTag, "Failed checksum test for file: " + file + ": file not found");
+                return false;
+            }
+            long actualChecksum = FileUtils.calculateChecksum(file);
+            if (actualChecksum != expectedChecksum) {
+                Slog.i(logTag, "Failed checksum test for file: " + file
+                        + ": required=" + expectedChecksum + ", actual=" + actualChecksum);
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/tzdata/update/src/test/libcore/tzdata/update/ConfigBundleTest.java b/tzdata/update/src/test/libcore/tzdata/update/ConfigBundleTest.java
new file mode 100644
index 0000000..f1325e7
--- /dev/null
+++ b/tzdata/update/src/test/libcore/tzdata/update/ConfigBundleTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import libcore.io.IoUtils;
+
+/**
+ * Tests for {@link ConfigBundle}.
+ */
+public class ConfigBundleTest extends TestCase {
+
+    private final List<File> testFiles = new ArrayList<>();
+
+    @Override
+    public void tearDown() throws Exception {
+        // Delete files / directories in reverse order.
+        Collections.reverse(testFiles);
+        for (File tempFile : testFiles) {
+            tempFile.delete();
+        }
+        super.tearDown();
+    }
+
+    public void testExtractZipSafely_goodZip() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
+            addZipEntry(zipOutputStream, "/leadingSlash");
+            addZipEntry(zipOutputStream, "absolute");
+            addZipEntry(zipOutputStream, "subDir/../file");
+            addZipEntry(zipOutputStream, "subDir/subDir/subDir/file");
+            addZipEntry(zipOutputStream, "subDir/subDir2/"); // Directory entry
+            addZipEntry(zipOutputStream, "subDir/../subDir3/"); // Directory entry
+        }
+        File dir = createTempDir();
+        File targetDir = new File(dir, "target");
+        TestInputStream inputStream =
+                new TestInputStream(new ByteArrayInputStream(baos.toByteArray()));
+        ConfigBundle.extractZipSafely(inputStream, targetDir, true /* makeWorldReadable */);
+        inputStream.assertClosed();
+        assertFilesExist(
+                new File(targetDir, "leadingSlash"),
+                new File(targetDir, "absolute"),
+                new File(targetDir, "file"),
+                new File(targetDir, "subDir/subDir/subDir/file"));
+        assertDirsExist(
+                new File(targetDir, "subDir/subDir2"),
+                new File(targetDir, "subDir3"));
+    }
+
+    public void testExtractZipSafely_badZip_fileOutsideTarget() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
+            addZipEntry(zipOutputStream, "../one");
+        }
+        doExtractZipFails(baos);
+    }
+
+    public void testExtractZipSafely_badZip_dirOutsideTarget() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
+            addZipEntry(zipOutputStream, "../one/");
+        }
+        doExtractZipFails(baos);
+    }
+
+    private void doExtractZipFails(ByteArrayOutputStream baos) {
+        File dir = createTempDir();
+        File targetDir = new File(dir, "target");
+        TestInputStream inputStream = new TestInputStream(
+                new ByteArrayInputStream(baos.toByteArray()));
+        try {
+            ConfigBundle.extractZipSafely(inputStream, targetDir, true /* makeWorldReadable */);
+            fail();
+        } catch (IOException expected) {
+        }
+        inputStream.assertClosed();
+    }
+
+    private static void addZipEntry(ZipOutputStream zipOutputStream, String name)
+            throws IOException {
+        ZipEntry zipEntry = new ZipEntry(name);
+        zipOutputStream.putNextEntry(zipEntry);
+        if (!zipEntry.isDirectory()) {
+            zipOutputStream.write('a');
+        }
+    }
+
+    private File createTempDir() {
+        final String tempPrefix = getClass().getSimpleName();
+        File tempDir = IoUtils.createTemporaryDirectory(tempPrefix);
+        testFiles.add(tempDir);
+        return tempDir;
+    }
+
+    private static void assertFilesExist(File... files) {
+        for (File f : files) {
+            assertTrue(f + " file expected to exist", f.exists() && f.isFile());
+        }
+    }
+
+    private static void assertDirsExist(File... dirs) {
+        for (File dir : dirs) {
+            assertTrue(dir + " directory expected to exist", dir.exists() && dir.isDirectory());
+        }
+    }
+
+    private static class TestInputStream extends FilterInputStream {
+
+        private boolean closed;
+
+        public TestInputStream(InputStream in) {
+            super(in);
+        }
+
+        @Override
+        public void close() throws IOException {
+            closed = true;
+            super.close();
+        }
+
+        public void assertClosed() {
+            assertTrue(closed);
+        }
+    }
+}
diff --git a/tzdata/update/src/test/libcore/tzdata/update/FileUtilsTest.java b/tzdata/update/src/test/libcore/tzdata/update/FileUtilsTest.java
new file mode 100644
index 0000000..ce02bfe
--- /dev/null
+++ b/tzdata/update/src/test/libcore/tzdata/update/FileUtilsTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update;
+
+import junit.framework.TestCase;
+
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
+/**
+ * Tests for {@link FileUtils}.
+ */
+public class FileUtilsTest extends TestCase {
+
+    private List<File> testFiles = new ArrayList<>();
+
+    @Override
+    public void tearDown() throws Exception {
+        // Delete in reverse order
+        Collections.reverse(testFiles);
+        for (File tempFile : testFiles) {
+            tempFile.delete();
+        }
+        super.tearDown();
+    }
+
+    public void testCalculateChecksum() throws Exception {
+        final String content = "Content";
+        File file1 = createTextFile(content);
+        File file2 = createTextFile(content);
+        File file3 = createTextFile(content + "!");
+
+        long file1CheckSum = FileUtils.calculateChecksum(file1);
+        long file2CheckSum = FileUtils.calculateChecksum(file2);
+        long file3Checksum = FileUtils.calculateChecksum(file3);
+
+        assertEquals(file1CheckSum, file2CheckSum);
+        assertTrue(file1CheckSum != file3Checksum);
+    }
+
+    public void testDeleteRecursive() throws Exception {
+        File dir = createTempDir();
+        File file1 = createRegularFile(dir, "file1");
+        File file2 = createRegularFile(dir, "file2");
+        File symLink1 = createSymlink(file1, dir, "symLink1");
+        File subDir = createDir(dir, "subDir");
+        File file3 = createRegularFile(subDir, "subFile1");
+        File file4 = createRegularFile(subDir, "subFile2");
+        File symLink2 = createSymlink(file1, dir, "symLink2");
+
+        File otherDir = createTempDir();
+        File otherFile = createRegularFile(otherDir, "kept");
+
+        File linkToOtherDir = createSymlink(otherDir, subDir, "linkToOtherDir");
+        File linkToOtherFile = createSymlink(otherFile, subDir, "linkToOtherFile");
+
+        File[] filesToDelete = { dir, file1, file2, symLink1, subDir, file3, file4, symLink2,
+                linkToOtherDir, linkToOtherFile };
+        File[] filesToKeep = { otherDir, otherFile };
+        assertFilesExist(filesToDelete);
+        assertFilesExist(filesToKeep);
+
+        FileUtils.deleteRecursive(dir);
+        assertFilesDoNotExist(filesToDelete);
+        assertFilesExist(filesToKeep);
+    }
+
+    public void testIsSymlink() throws Exception {
+        File dir = createTempDir();
+        File subDir = createDir(dir, "subDir");
+        File fileInSubDir = createRegularFile(subDir, "fileInSubDir");
+        File normalFile = createRegularFile(dir, "normalFile");
+        File symlinkToDir = createSymlink(subDir, dir, "symlinkToDir");
+        File symlinkToFile = createSymlink(fileInSubDir, dir, "symlinkToFile");
+        File symlinkToFileInSubDir = createSymlink(fileInSubDir, dir, "symlinkToFileInSubDir");
+        File normalFileViaSymlink = new File(symlinkToDir, "normalFile");
+
+        assertFalse(FileUtils.isSymlink(dir));
+        assertFalse(FileUtils.isSymlink(subDir));
+        assertFalse(FileUtils.isSymlink(fileInSubDir));
+        assertFalse(FileUtils.isSymlink(normalFile));
+        assertTrue(FileUtils.isSymlink(symlinkToDir));
+        assertTrue(FileUtils.isSymlink(symlinkToFile));
+        assertTrue(FileUtils.isSymlink(symlinkToFileInSubDir));
+        assertFalse(FileUtils.isSymlink(normalFileViaSymlink));
+    }
+
+    public void testCreateSubFile() throws Exception {
+        File dir1 = createTempDir();
+        File subFile = FileUtils.createSubFile(dir1, "file");
+        assertFileCanonicalEquals(new File(dir1, "file"), subFile);
+
+        assertCreateSubFileThrows(dir1, "../file");
+        assertCreateSubFileThrows(dir1, "../../file");
+        assertCreateSubFileThrows(dir1, "../otherdir/file");
+
+        File dir2 = createTempDir();
+        File dir2Subdir = createDir(dir2, "dir2Subdir");
+        File expectedSymlinkToDir2 = createSymlink(dir2Subdir, dir1, "symlinkToDir2");
+
+        File actualSymlinkToDir2 = FileUtils.createSubFile(dir1, "symlinkToDir2");
+        assertEquals(expectedSymlinkToDir2, actualSymlinkToDir2);
+
+        assertCreateSubFileThrows(dir1, "symlinkToDir2/fileInSymlinkedDir");
+    }
+
+    public void testEnsureDirectoryExists() throws Exception {
+        File dir = createTempDir();
+
+        File exists = new File(dir, "exists");
+        assertTrue(exists.mkdir());
+        assertTrue(exists.setReadable(true /* readable */, true /* ownerOnly */));
+        assertTrue(exists.setExecutable(true /* readable */, true /* ownerOnly */));
+        FileUtils.ensureDirectoriesExist(exists, true /* makeWorldReadable */);
+        assertDirExistsAndIsAccessible(exists, false /* requireWorldReadable */);
+
+        File subDir = new File(dir, "subDir");
+        assertFalse(subDir.exists());
+        FileUtils.ensureDirectoriesExist(subDir, true /* makeWorldReadable */);
+        assertDirExistsAndIsAccessible(subDir, true /* requireWorldReadable */);
+
+        File one = new File(dir, "one");
+        File two = new File(one, "two");
+        File three = new File(two, "three");
+        FileUtils.ensureDirectoriesExist(three, true /* makeWorldReadable */);
+        assertDirExistsAndIsAccessible(one, true /* requireWorldReadable */);
+        assertDirExistsAndIsAccessible(two, true /* requireWorldReadable */);
+        assertDirExistsAndIsAccessible(three, true /* requireWorldReadable */);
+    }
+
+    public void testEnsureDirectoriesExist_noPermissions() throws Exception {
+        File dir = createTempDir();
+        assertDirExistsAndIsAccessible(dir, false /* requireWorldReadable */);
+
+        File unreadableSubDir = new File(dir, "unreadableSubDir");
+        assertTrue(unreadableSubDir.mkdir());
+        assertTrue(unreadableSubDir.setReadable(false /* readable */, true /* ownerOnly */));
+        assertTrue(unreadableSubDir.setExecutable(false /* readable */, true /* ownerOnly */));
+
+        File toCreate = new File(unreadableSubDir, "toCreate");
+        try {
+            FileUtils.ensureDirectoriesExist(toCreate, true /* makeWorldReadable */);
+            fail();
+        } catch (IOException expected) {
+        }
+        assertDirExistsAndIsAccessible(dir, false /* requireWorldReadable */);
+        assertFalse(unreadableSubDir.canRead() && unreadableSubDir.canExecute());
+        assertFalse(toCreate.exists());
+    }
+
+    public void testEnsureFileDoesNotExist() throws Exception {
+        File dir = createTempDir();
+
+        FileUtils.ensureFileDoesNotExist(new File(dir, "doesNotExist"));
+
+        File exists1 = createRegularFile(dir, "exists1");
+        assertTrue(exists1.exists());
+        FileUtils.ensureFileDoesNotExist(exists1);
+        assertFalse(exists1.exists());
+
+        exists1 = createRegularFile(dir, "exists1");
+        File symlink = createSymlink(exists1, dir, "symlinkToFile");
+        assertTrue(symlink.exists());
+        FileUtils.ensureFileDoesNotExist(symlink);
+        assertFalse(symlink.exists());
+        assertTrue(exists1.exists());
+
+        // Only files and symlinks supported. We do not delete directories.
+        File emptyDir = createTempDir();
+        try {
+            FileUtils.ensureFileDoesNotExist(emptyDir);
+            fail();
+        } catch (IOException expected) {
+        }
+        assertTrue(emptyDir.exists());
+    }
+
+    // This test does not pass when run as root because root can do anything even if the permissions
+    // don't allow it.
+    public void testEnsureFileDoesNotExist_noPermission() throws Exception {
+        File dir = createTempDir();
+
+        File protectedDir = createDir(dir, "protected");
+        File undeletable = createRegularFile(protectedDir, "undeletable");
+        assertTrue(protectedDir.setWritable(false));
+        assertTrue(undeletable.exists());
+        try {
+            FileUtils.ensureFileDoesNotExist(undeletable);
+            fail();
+        } catch (IOException expected) {
+        } finally {
+            assertTrue(protectedDir.setWritable(true)); // Reset for clean-up
+        }
+        assertTrue(undeletable.exists());
+    }
+
+    public void testCheckFilesExist() throws Exception {
+        File dir = createTempDir();
+        createRegularFile(dir, "exists1");
+        File subDir = createDir(dir, "subDir");
+        createRegularFile(subDir, "exists2");
+        assertTrue(FileUtils.filesExist(dir, "exists1", "subDir/exists2"));
+        assertFalse(FileUtils.filesExist(dir, "doesNotExist"));
+        assertFalse(FileUtils.filesExist(dir, "subDir/doesNotExist"));
+    }
+
+    public void testReadLines() throws Exception {
+        File file = createTextFile("One\nTwo\nThree\n");
+
+        List<String> lines = FileUtils.readLines(file);
+        assertEquals(3, lines.size());
+        assertEquals(lines, Arrays.asList("One", "Two", "Three"));
+    }
+
+    private File createTextFile(String contents) throws IOException {
+        File file = File.createTempFile(getClass().getSimpleName(), ".txt");
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            BufferedWriter writer = new BufferedWriter(
+                    new OutputStreamWriter(fos, StandardCharsets.UTF_8));
+            writer.write(contents);
+            writer.close();
+        }
+        return file;
+    }
+
+    private File createSymlink(File file, File symlinkDir, String symlinkName) throws Exception {
+        assertTrue(file.exists());
+
+        File symlink = new File(symlinkDir, symlinkName);
+        Os.symlink(file.getAbsolutePath(), symlink.getAbsolutePath());
+        testFiles.add(symlink);
+        return symlink;
+    }
+
+    private static void assertCreateSubFileThrows(File parentDir, String name) {
+        try {
+            FileUtils.createSubFile(parentDir, name);
+            fail();
+        } catch (IOException expected) {
+            assertTrue(expected.getMessage().contains("must exist beneath"));
+        }
+    }
+
+    private static void assertFilesDoNotExist(File... files) {
+        for (File f : files) {
+            assertFalse(f + " unexpectedly exists", f.exists());
+        }
+    }
+
+    private static void assertFilesExist(File... files) {
+        for (File f : files) {
+            assertTrue(f + " expected to exist", f.exists());
+        }
+    }
+
+    private static void assertDirExistsAndIsAccessible(File dir, boolean requireWorldReadable)
+            throws Exception {
+        assertTrue(dir.exists() && dir.isDirectory() && dir.canRead() && dir.canExecute());
+
+        String path = dir.getCanonicalPath();
+        StructStat sb = Libcore.os.stat(path);
+        int mask = OsConstants.S_IXUSR | OsConstants.S_IRUSR;
+        if (requireWorldReadable) {
+            mask = mask | OsConstants.S_IXGRP | OsConstants.S_IRGRP
+                    | OsConstants.S_IXOTH | OsConstants.S_IROTH;
+        }
+        assertTrue("Permission mask required: " + Integer.toOctalString(mask),
+                (sb.st_mode & mask) == mask);
+    }
+
+    private static void assertFileCanonicalEquals(File expected, File actual) throws IOException {
+        assertEquals(expected.getCanonicalFile(), actual.getCanonicalFile());
+    }
+
+    private File createTempDir() {
+        final String tempPrefix = getClass().getSimpleName();
+        File tempDir = IoUtils.createTemporaryDirectory(tempPrefix);
+        testFiles.add(tempDir);
+        return tempDir;
+    }
+
+    private File createDir(File parentDir, String name) {
+        File dir = new File(parentDir, name);
+        assertTrue(dir.mkdir());
+        testFiles.add(dir);
+        return dir;
+    }
+
+    private File createRegularFile(File dir, String name) throws Exception {
+        File file = new File(dir, name);
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            fos.write("Hello".getBytes());
+        }
+        testFiles.add(file);
+        return file;
+    }
+}
diff --git a/tzdata/update/src/test/libcore/tzdata/update/TzDataBundleInstallerTest.java b/tzdata/update/src/test/libcore/tzdata/update/TzDataBundleInstallerTest.java
new file mode 100644
index 0000000..1825bb3
--- /dev/null
+++ b/tzdata/update/src/test/libcore/tzdata/update/TzDataBundleInstallerTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import libcore.tzdata.update.tools.TzDataBundleBuilder;
+
+/**
+ * Tests for {@link libcore.tzdata.update.TzDataBundleInstaller}.
+ */
+public class TzDataBundleInstallerTest extends TestCase {
+
+    private static final File SYSTEM_ZONE_INFO_FILE = new File("/system/usr/share/zoneinfo/tzdata");
+
+    private TzDataBundleInstaller installer;
+    private File tempDir;
+    private File testInstallDir;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        tempDir = createDirectory("tempDir");
+        testInstallDir =  createDirectory("testInstall");
+        installer = new TzDataBundleInstaller("TzDataBundleInstallerTest", testInstallDir);
+    }
+
+    private static File createDirectory(String prefix) throws IOException {
+        File dir = File.createTempFile(prefix, "");
+        assertTrue(dir.delete());
+        assertTrue(dir.mkdir());
+        return dir;
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        if (testInstallDir.exists()) {
+            FileUtils.deleteRecursive(testInstallDir);
+        }
+        if (tempDir.exists()) {
+            FileUtils.deleteRecursive(tempDir);
+        }
+        super.tearDown();
+    }
+
+    /** Tests the first update on a device */
+    public void testSuccessfulFirstUpdate() throws Exception {
+        ConfigBundle tzData = createValidTzDataBundle("2030a");
+
+        assertTrue(install(tzData));
+        assertTzDataInstalled(tzData);
+    }
+
+    /**
+     * Tests an update on a device when there is a prior update already applied.
+     */
+    public void testSuccessfulFollowOnUpdate() throws Exception {
+        ConfigBundle tzData1 = createValidTzDataBundle("2030a");
+        assertTrue(install(tzData1));
+        assertTzDataInstalled(tzData1);
+
+        ConfigBundle tzData2 = createValidTzDataBundle("2030b");
+        assertTrue(install(tzData2));
+        assertTzDataInstalled(tzData2);
+    }
+
+
+    /** Tests that a bundle with a missing file will not update the content. */
+    public void testMissingRequiredBundleFile() throws Exception {
+        ConfigBundle installedConfigBundle = createValidTzDataBundle("2030a");
+        assertTrue(install(installedConfigBundle));
+        assertTzDataInstalled(installedConfigBundle);
+
+        ConfigBundle incompleteUpdate =
+                createValidTzDataBundleBuilder("2030b").clearBionicTzData().buildUnvalidated();
+        assertFalse(install(incompleteUpdate));
+        assertTzDataInstalled(installedConfigBundle);
+    }
+
+    /**
+     * Tests that an update will be unpacked even if there is a partial update from a previous run.
+     */
+    public void testInstallWithWorkingDir() throws Exception {
+        File workingDir = new File(testInstallDir, TzDataBundleInstaller.WORKING_DIR_NAME);
+        assertTrue(workingDir.mkdir());
+        createFile(new File(workingDir, "myFile"));
+
+        ConfigBundle tzData = createValidTzDataBundle("2030a");
+        assertTrue(install(tzData));
+        assertTzDataInstalled(tzData);
+    }
+
+    /**
+     * Tests that a bundle with a checksum entry that references a missing file will not update the
+     * content.
+     */
+    public void testChecksumBundleEntry_fileMissing() throws Exception {
+        ConfigBundle badUpdate =
+                createValidTzDataBundleBuilder("2030b")
+                        .addChecksum("/fileDoesNotExist", 1234)
+                        .build();
+        assertFalse(install(badUpdate));
+        assertNoContentInstalled();
+    }
+
+    /**
+     * Tests that a bundle with a checksum entry with a bad checksum will not update the
+     * content.
+     */
+    public void testChecksumBundleEntry_incorrectChecksum() throws Exception {
+        File fileToChecksum = SYSTEM_ZONE_INFO_FILE;
+        long badChecksum = FileUtils.calculateChecksum(fileToChecksum) + 1;
+        ConfigBundle badUpdate =
+                createValidTzDataBundleBuilder("2030b")
+                        .clearChecksumEntries()
+                        .addChecksum(fileToChecksum.getPath(), badChecksum)
+                        .build();
+        assertFalse(install(badUpdate));
+        assertNoContentInstalled();
+    }
+
+    private boolean install(ConfigBundle configBundle) throws Exception {
+        return installer.install(configBundle.getBundleBytes());
+    }
+
+    private ConfigBundle createValidTzDataBundle(String tzDataVersion)
+            throws IOException {
+        return createValidTzDataBundleBuilder(tzDataVersion).build();
+    }
+
+    private TzDataBundleBuilder createValidTzDataBundleBuilder(String tzDataVersion)
+            throws IOException {
+
+        // The file to include in the installation-time checksum check.
+        File fileToChecksum = SYSTEM_ZONE_INFO_FILE;
+        long checksum = FileUtils.calculateChecksum(fileToChecksum);
+
+        File bionicTzData = new File(tempDir, "zoneinfo");
+        createFile(bionicTzData);
+
+        File icuData = new File(tempDir, "icudata");
+        createFile(icuData);
+
+        return new TzDataBundleBuilder()
+                .addChecksum(fileToChecksum.getPath(), checksum)
+                .setTzDataVersion(tzDataVersion)
+                .addBionicTzData(bionicTzData)
+                .addIcuTzData(icuData);
+    }
+
+    private void assertTzDataInstalled(ConfigBundle expectedTzData) throws Exception {
+        assertTrue(testInstallDir.exists());
+
+        File currentTzDataDir = new File(testInstallDir, TzDataBundleInstaller.CURRENT_TZ_DATA_DIR_NAME);
+        assertTrue(currentTzDataDir.exists());
+
+        File checksumFile = new File(currentTzDataDir, ConfigBundle.CHECKSUMS_FILE_NAME);
+        assertTrue(checksumFile.exists());
+
+        File versionFile = new File(currentTzDataDir,
+                ConfigBundle.TZ_DATA_VERSION_FILE_NAME);
+        assertTrue(versionFile.exists());
+
+        File bionicFile = new File(currentTzDataDir, ConfigBundle.ZONEINFO_FILE_NAME);
+        assertTrue(bionicFile.exists());
+
+        File icuFile = new File(currentTzDataDir, ConfigBundle.ICU_DATA_FILE_NAME);
+        assertTrue(icuFile.exists());
+
+        // Also check no working directory is left lying around.
+        File workingDir = new File(testInstallDir, TzDataBundleInstaller.WORKING_DIR_NAME);
+        assertFalse(workingDir.exists());
+    }
+
+    private void assertNoContentInstalled() {
+        File currentTzDataDir = new File(testInstallDir, TzDataBundleInstaller.CURRENT_TZ_DATA_DIR_NAME);
+        assertFalse(currentTzDataDir.exists());
+
+        // Also check no working directories are left lying around.
+        File workingDir = new File(testInstallDir, TzDataBundleInstaller.WORKING_DIR_NAME);
+        assertFalse(workingDir.exists());
+
+        File oldDataDir = new File(testInstallDir, TzDataBundleInstaller.OLD_TZ_DATA_DIR_NAME);
+        assertFalse(oldDataDir.exists());
+    }
+
+    private static void createFile(File file) {
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            fos.write('a');
+        } catch (IOException e) {
+            fail(e.getMessage());
+        }
+    }
+}
diff --git a/tzdata/update_test_app/Android.mk b/tzdata/update_test_app/Android.mk
new file mode 100644
index 0000000..ee70819
--- /dev/null
+++ b/tzdata/update_test_app/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := UpdateTestApp
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/tzdata/update_test_app/AndroidManifest.xml b/tzdata/update_test_app/AndroidManifest.xml
new file mode 100644
index 0000000..67a8450
--- /dev/null
+++ b/tzdata/update_test_app/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="libcore.tzdata.update_test_app.installupdatetestapp" >
+
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+    <application
+        android:allowBackup="false"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@android:style/Theme.Holo.Light">
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="libcore.tzdata.update_test_app.fileprovider"
+            android:grantUriPermissions="true"
+            android:exported="false">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/filepaths" />
+        </provider>
+
+    </application>
+
+</manifest>
diff --git a/tzdata/update_test_app/res/drawable/ic_launcher.png b/tzdata/update_test_app/res/drawable/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/tzdata/update_test_app/res/drawable/ic_launcher.png
Binary files differ
diff --git a/tzdata/update_test_app/res/layout/activity_main.xml b/tzdata/update_test_app/res/layout/activity_main.xml
new file mode 100644
index 0000000..b265837
--- /dev/null
+++ b/tzdata/update_test_app/res/layout/activity_main.xml
@@ -0,0 +1,106 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context=".MainActivity">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/action"
+            android:id="@+id/action_label" />
+
+        <EditText
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/action"
+            android:layout_weight="1"
+            android:text="@string/default_action" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/version"
+            android:id="@+id/version_label" />
+
+        <EditText
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/version"
+            android:layout_weight="1"
+            android:text="@string/default_version" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/content_path"
+            android:id="@+id/content_path_label" />
+
+        <EditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/default_content_path"
+            android:id="@+id/content_path" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/required_hash"
+            android:id="@+id/required_hash_label" />
+
+        <EditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/default_required_hash"
+            android:id="@+id/required_hash" />
+
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/trigger_install_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
+        android:text="@string/trigger_install" />
+
+    <ScrollView
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:scrollbars="vertical"
+            android:fillViewport="true">
+
+        <TextView
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:id="@+id/log"
+                android:singleLine="false" />
+
+    </ScrollView>
+
+</LinearLayout>
diff --git a/tzdata/update_test_app/res/values/strings.xml b/tzdata/update_test_app/res/values/strings.xml
new file mode 100644
index 0000000..524f9d8
--- /dev/null
+++ b/tzdata/update_test_app/res/values/strings.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">InstallUpdateTestApp</string>
+    <string name="action">Action</string>
+    <string name="content_path">Content Path</string>
+    <string name="default_action">android.intent.action.UPDATE_TZDATA</string>
+    <string name="default_content_path">/data/local/tmp/out.zip</string>
+    <string name="default_required_hash">NONE</string>
+    <string name="default_version">1</string>
+    <string name="required_hash">Required Hash</string>
+    <string name="trigger_install">Trigger Install</string>
+    <string name="version">Version</string>
+</resources>
diff --git a/tzdata/update_test_app/res/xml/filepaths.xml b/tzdata/update_test_app/res/xml/filepaths.xml
new file mode 100644
index 0000000..c95b8f4
--- /dev/null
+++ b/tzdata/update_test_app/res/xml/filepaths.xml
@@ -0,0 +1,4 @@
+<!-- Used by FileProvider. See AndroidManifest.xml -->
+<paths>
+    <files-path path="temp/" name="temp" />
+</paths>
diff --git a/tzdata/update_test_app/src/libcore/tzdata/update_test_app/installupdatetestapp/MainActivity.java b/tzdata/update_test_app/src/libcore/tzdata/update_test_app/installupdatetestapp/MainActivity.java
new file mode 100644
index 0000000..f9d911b
--- /dev/null
+++ b/tzdata/update_test_app/src/libcore/tzdata/update_test_app/installupdatetestapp/MainActivity.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update_test_app.installupdatetestapp;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.content.FileProvider;
+import android.util.Base64;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Date;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+
+public class MainActivity extends Activity implements View.OnClickListener {
+
+    private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate";
+    private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH";
+    private static final String EXTRA_SIGNATURE = "SIGNATURE";
+    private static final String EXTRA_VERSION_NUMBER = "VERSION";
+
+    public static final String TEST_CERT = "" +
+            "MIIDsjCCAxugAwIBAgIJAPLf2gS0zYGUMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYDVQQGEwJVUzET" +
+            "MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEPMA0GA1UEChMGR29v" +
+            "Z2xlMRAwDgYDVQQLEwd0ZXN0aW5nMRYwFAYDVQQDEw1HZXJlbXkgQ29uZHJhMSEwHwYJKoZIhvcN" +
+            "AQkBFhJnY29uZHJhQGdvb2dsZS5jb20wHhcNMTIwNzE0MTc1MjIxWhcNMTIwODEzMTc1MjIxWjCB" +
+            "mDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZp" +
+            "ZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHdGVzdGluZzEWMBQGA1UEAxMNR2VyZW15IENv" +
+            "bmRyYTEhMB8GCSqGSIb3DQEJARYSZ2NvbmRyYUBnb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA" +
+            "A4GNADCBiQKBgQCjGGHATBYlmas+0sEECkno8LZ1KPglb/mfe6VpCT3GhSr+7br7NG/ZwGZnEhLq" +
+            "E7YIH4fxltHmQC3Tz+jM1YN+kMaQgRRjo/LBCJdOKaMwUbkVynAH6OYsKevjrOPk8lfM5SFQzJMG" +
+            "sA9+Tfopr5xg0BwZ1vA/+E3mE7Tr3M2UvwIDAQABo4IBADCB/TAdBgNVHQ4EFgQUhzkS9E6G+x8W" +
+            "L4EsmRjDxu28tHUwgc0GA1UdIwSBxTCBwoAUhzkS9E6G+x8WL4EsmRjDxu28tHWhgZ6kgZswgZgx" +
+            "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3" +
+            "MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB3Rlc3RpbmcxFjAUBgNVBAMTDUdlcmVteSBDb25k" +
+            "cmExITAfBgkqhkiG9w0BCQEWEmdjb25kcmFAZ29vZ2xlLmNvbYIJAPLf2gS0zYGUMAwGA1UdEwQF" +
+            "MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYiugFDmbDOQ2U/+mqNt7o8ftlEo9SJrns6O8uTtK6AvR" +
+            "orDrR1AXTXkuxwLSbmVfedMGOZy7Awh7iZa8hw5x9XmUudfNxvmrKVEwGQY2DZ9PXbrnta/dwbhK" +
+            "mWfoepESVbo7CKIhJp8gRW0h1Z55ETXD57aGJRvQS4pxkP8ANhM=";
+
+
+    public static final String TEST_KEY = "" +
+            "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKMYYcBMFiWZqz7SwQQKSejwtnUo" +
+            "+CVv+Z97pWkJPcaFKv7tuvs0b9nAZmcSEuoTtggfh/GW0eZALdPP6MzVg36QxpCBFGOj8sEIl04p" +
+            "ozBRuRXKcAfo5iwp6+Os4+TyV8zlIVDMkwawD35N+imvnGDQHBnW8D/4TeYTtOvczZS/AgMBAAEC" +
+            "gYBxwFalNSwZK3WJipq+g6KLCiBn1JxGGDQlLKrweFaSuFyFky9fd3IvkIabirqQchD612sMb+GT" +
+            "0t1jptW6z4w2w6++IW0A3apDOCwoD+uvDBXrbFqI0VbyAWUNqHVdaFFIRk2IHGEE6463mGRdmILX" +
+            "IlCd/85RTHReg4rl/GFqWQJBANgLAIR4pWbl5Gm+DtY18wp6Q3pJAAMkmP/lISCBIidu1zcqYIKt" +
+            "PoDW4Knq9xnhxPbXrXKv4YzZWHBK8GkKhQ0CQQDBQnXufQcMew+PwiS0oJvS+eQ6YJwynuqG2ejg" +
+            "WE+T7489jKtscRATpUXpZUYmDLGg9bLt7L62hFvFSj2LO2X7AkBcdrD9AWnBFWlh/G77LVHczSEu" +
+            "KCoyLiqxcs5vy/TjLaQ8vw1ZQG580/qJnr+tOxyCjSJ18GK3VppsTRaBznfNAkB3nuCKNp9HTWCL" +
+            "dfrsRsFMrFpk++mSt6SoxXaMbn0LL2u1CD4PCEiQMGt+lK3/3TmRTKNs+23sYS7Ahjxj0udDAkEA" +
+            "p57Nj65WNaWeYiOfTwKXkLj8l29H5NbaGWxPT0XkWr4PvBOFZVH/wj0/qc3CMVGnv11+DyO+QUCN" +
+            "SqBB5aRe8g==";
+
+    private EditText actionEditText;
+    private EditText versionEditText;
+    private EditText contentPathEditText;
+    private EditText requiredHashEditText;
+    private TextView logView;
+
+    private ExecutorService executor;
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        Button triggerInstallButton = (Button) findViewById(R.id.trigger_install_button);
+        triggerInstallButton.setOnClickListener(this);
+
+        actionEditText = (EditText) findViewById(R.id.action);
+        versionEditText = (EditText) findViewById(R.id.version);
+        contentPathEditText = (EditText) findViewById(R.id.content_path);
+        requiredHashEditText = (EditText) findViewById(R.id.required_hash);
+        logView = (TextView) findViewById(R.id.log);
+        executor = Executors.newFixedThreadPool(1);
+    }
+
+    @Override
+    public void onClick(View v) {
+        final String action = actionEditText.getText().toString();
+        final String contentPath = contentPathEditText.getText().toString();
+        final String version = versionEditText.getText().toString();
+        final String requiredHash = requiredHashEditText.getText().toString();
+
+        new AsyncTask<Void, String, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                final File contentFile = new File(contentPath);
+                File tempDir = new File(getFilesDir(), "temp");
+                if (!tempDir.exists() && !tempDir.mkdir()) {
+                    publishProgress("Unable to create: " + tempDir);
+                    return null;
+                }
+
+                File copyOfContentFile;
+                try {
+                    copyOfContentFile = File.createTempFile("content", ".tmp", tempDir);
+                    copyFile(contentFile, copyOfContentFile);
+                } catch (IOException e) {
+                    publishProgress("Error", exceptionToString(e));
+                    return null;
+                }
+                publishProgress("Created copy of " + contentFile + " at " + copyOfContentFile);
+
+                String originalCert = null;
+                try {
+                    originalCert = overrideCert(TEST_CERT);
+                    sleep(1000);
+                    publishProgress("Overridden update cert");
+
+                    String signature = createSignature(copyOfContentFile, version, requiredHash);
+                    sendIntent(copyOfContentFile, action, version, requiredHash, signature);
+                    publishProgress("Sent update intent");
+                } catch (Exception e) {
+                    publishProgress("Error", exceptionToString(e));
+                } finally {
+                    if (originalCert != null) {
+                        sleep(1000);
+                        try {
+                            overrideCert(originalCert);
+                            publishProgress("Reverted update cert");
+                        } catch (Exception e) {
+                            publishProgress("Unable to revert update cert", exceptionToString(e));
+                        }
+                    }
+                }
+                publishProgress("Update intent sent successfully");
+                return null;
+            }
+
+            @Override
+            protected void onProgressUpdate(String... values) {
+                for (String message : values) {
+                    addToLog(message, null);
+                }
+            }
+        }.executeOnExecutor(executor);
+    }
+
+    private String overrideCert(String cert) throws Exception {
+        final String key = UPDATE_CERTIFICATE_KEY;
+        String originalCert = Settings.Secure.getString(getContentResolver(), key);
+        if (!Settings.Secure.putString(getContentResolver(), key, cert)) {
+            throw new Exception("Unable to override update certificate");
+        }
+        return originalCert;
+    }
+
+    private void sleep(long millisDelay) {
+        try {
+            Thread.sleep(millisDelay);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+    }
+
+    private void sendIntent(
+            File contentFile, String action, String version, String required, String sig) {
+        Intent i = new Intent();
+        i.setAction(action);
+        Uri contentUri =
+                FileProvider.getUriForFile(
+                        getApplicationContext(), "libcore.tzdata.update_test_app.fileprovider",
+                        contentFile);
+        i.setData(contentUri);
+        i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        i.putExtra(EXTRA_VERSION_NUMBER, version);
+        i.putExtra(EXTRA_REQUIRED_HASH, required);
+        i.putExtra(EXTRA_SIGNATURE, sig);
+        sendBroadcast(i);
+    }
+
+    private void addToLog(String message, Exception e) {
+        logString(message);
+        if (e != null) {
+            String text = exceptionToString(e);
+            logString(text);
+        }
+    }
+
+    private void logString(String value) {
+        logView.append(new Date() + " " + value + "\n");
+        int scrollAmount =
+                logView.getLayout().getLineTop(logView.getLineCount()) - logView.getHeight();
+        logView.scrollTo(0, scrollAmount);
+    }
+
+    private static String createSignature(File contentFile, String version, String requiredHash)
+            throws Exception {
+        byte[] contentBytes = readBytes(contentFile);
+        Signature signer = Signature.getInstance("SHA512withRSA");
+        signer.initSign(createKey());
+        signer.update(contentBytes);
+        signer.update(version.trim().getBytes());
+        signer.update(requiredHash.getBytes());
+        return new String(Base64.encode(signer.sign(), Base64.DEFAULT));
+    }
+
+    private static byte[] readBytes(File contentFile) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (FileInputStream fis = new FileInputStream(contentFile)) {
+            int count;
+            byte[] buffer = new byte[8192];
+            while ((count = fis.read(buffer)) != -1) {
+                baos.write(buffer, 0, count);
+            }
+        }
+        return baos.toByteArray();
+    }
+
+    private static PrivateKey createKey() throws Exception {
+        byte[] derKey = Base64.decode(TEST_KEY.getBytes(), Base64.DEFAULT);
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(derKey);
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        return keyFactory.generatePrivate(keySpec);
+    }
+
+    private static String exceptionToString(Exception e) {
+        StringWriter writer = new StringWriter();
+        e.printStackTrace(new PrintWriter(writer));
+        return writer.getBuffer().toString();
+    }
+
+    private static void copyFile(File from, File to) throws IOException {
+        byte[] buffer = new byte[8192];
+        int count;
+        try (
+                FileInputStream in = new FileInputStream(from);
+                FileOutputStream out = new FileOutputStream(to)
+        ) {
+            while ((count = in.read(buffer)) != -1) {
+                out.write(buffer, 0, count);
+            }
+        }
+    }
+}