Merge "Changes to enable timezone data overrides for ICU and libcore"
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 9987055..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,11 +113,19 @@
 LOCAL_MODULE := core-libart
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
 LOCAL_REQUIRED_MODULES := tzdata
-# Should not be dex-preopted as it isn't really a Dalvik boot jar or a
-# regular java library, but part of the image for ART.
-LOCAL_DEX_PREOPT := false
+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)
@@ -143,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
@@ -152,9 +189,7 @@
 LOCAL_MODULE := core-libart-hostdex
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
 LOCAL_REQUIRED_MODULES := tzdata-host
-# Should not be dex-preopted as it isn't really a Dalvik boot jar or a
-# regular java library, but part of the image for ART.
-LOCAL_DEX_PREOPT := false
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
 include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
 
 # Make the core-tests library.
diff --git a/NativeCode.mk b/NativeCode.mk
index 3a90afb..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).
@@ -107,6 +106,37 @@
 
 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.
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..b58077d
--- /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) {
+            com.android.ibm.icu.text.BreakIterator it =
+                com.android.ibm.icu.text.BreakIterator.getLineInstance(text.locale);
+            it.setText(text.text);
+
+            while (it.next() != com.android.ibm.icu.text.BreakIterator.DONE) {
+            }
+        }
+    }
+}
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..3ffd634 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
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/expectations/knownfailures.txt b/expectations/knownfailures.txt
index c6d3dd7..c23deff 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1492,5 +1492,14 @@
   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/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/MulticastSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
index e3e1207..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,13 +49,13 @@
 
     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");
 
     private NetworkInterface loopbackInterface;
     private NetworkInterface ipv4NetworkInterface;
     private NetworkInterface ipv6NetworkInterface;
+    private boolean supportsMulticast;
 
     @Override
     protected void setUp() throws Exception {
@@ -69,6 +69,14 @@
         Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
         assertNotNull(interfaces);
 
+        // Determine if the device is marked to support multicast or not. If this propery is not
+        // set we assume the device has an interface capable of supporting multicast.
+        supportsMulticast = Boolean.valueOf(
+                System.getProperty("android.cts.device.multicast", "true"));
+        if (!supportsMulticast) {
+            return;
+        }
+
         while (interfaces.hasMoreElements()
                 && (ipv4NetworkInterface == null || ipv6NetworkInterface == null)) {
             NetworkInterface nextInterface = interfaces.nextElement();
@@ -78,18 +86,22 @@
                     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);
     }
 
     public void test_Constructor() throws IOException {
+        if (!supportsMulticast) {
+            return;
+        }
         // Regression test for 497.
         MulticastSocket s = new MulticastSocket();
         // Regression test for Harmony-1162.
@@ -99,6 +111,9 @@
     }
 
     public void test_ConstructorI() throws IOException {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket orig = new MulticastSocket();
         int port = orig.getLocalPort();
         orig.close();
@@ -110,6 +125,9 @@
     }
 
     public void test_getInterface() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         // Validate that we get the expected response when one was not set.
         MulticastSocket mss = new MulticastSocket(0);
         // We expect an ANY address in this case.
@@ -134,6 +152,9 @@
     }
 
     public void test_getNetworkInterface() throws IOException {
+        if (!supportsMulticast) {
+            return;
+        }
         // Validate that we get the expected response when one was not set.
         MulticastSocket mss = new MulticastSocket(0);
         NetworkInterface theInterface = mss.getNetworkInterface();
@@ -176,6 +197,9 @@
     }
 
     public void test_getTimeToLive() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket();
         mss.setTimeToLive(120);
         assertEquals("Returned incorrect 1st TTL", 120, mss.getTimeToLive());
@@ -185,6 +209,9 @@
     }
 
     public void test_getTTL() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket();
         mss.setTTL((byte) 120);
         assertEquals("Returned incorrect TTL", 120, mss.getTTL());
@@ -192,10 +219,16 @@
     }
 
     public void test_joinGroupLjava_net_InetAddress_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_joinGroupLjava_net_InetAddress(GOOD_IPv4);
     }
 
     public void test_joinGroupLjava_net_InetAddress_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_joinGroupLjava_net_InetAddress(GOOD_IPv6);
     }
 
@@ -220,6 +253,9 @@
     }
 
     public void test_joinGroup_null_null() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.joinGroup(null, null);
@@ -230,6 +266,9 @@
     }
 
     public void test_joinGroup_non_multicast_address_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.joinGroup(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0), null);
@@ -240,6 +279,9 @@
     }
 
     public void test_joinGroup_non_multicast_address_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.joinGroup(new InetSocketAddress(InetAddress.getByName("::1"), 0), null);
@@ -251,28 +293,47 @@
 
     public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4()
             throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 ipv4NetworkInterface, GOOD_IPv4, BAD_IPv4);
     }
 
     public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6()
             throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 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";
@@ -286,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());
@@ -310,6 +378,9 @@
     }
 
     public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         // Check that we can join on specific interfaces and that we only receive if data is
         // received on that interface. This test is only really useful on devices with multiple
         // non-loopback interfaces.
@@ -378,12 +449,18 @@
 
     public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv4()
             throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
                 ipv4NetworkInterface, GOOD_IPv4);
     }
 
     public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv6()
             throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
                 ipv6NetworkInterface, GOOD_IPv6);
     }
@@ -405,10 +482,16 @@
     }
 
     public void test_leaveGroupLjava_net_InetAddress_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_leaveGroupLjava_net_InetAddress(GOOD_IPv4);
     }
 
     public void test_leaveGroupLjava_net_InetAddress_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_leaveGroupLjava_net_InetAddress(GOOD_IPv6);
     }
 
@@ -428,6 +511,9 @@
     }
 
     public void test_leaveGroup_null_null() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.leaveGroup(null, null);
@@ -438,6 +524,9 @@
     }
 
     public void test_leaveGroup_non_multicast_address_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.leaveGroup(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0), null);
@@ -448,6 +537,9 @@
     }
 
     public void test_leaveGroup_non_multicast_address_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.leaveGroup(new InetSocketAddress(InetAddress.getByName("::1"), 0), null);
@@ -459,12 +551,18 @@
 
     public void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4()
             throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 ipv4NetworkInterface, GOOD_IPv4, BAD_IPv4);
     }
 
     public void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6()
             throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 ipv6NetworkInterface, GOOD_IPv6, BAD_IPv6);
     }
@@ -505,10 +603,16 @@
     }
 
     public void test_sendLjava_net_DatagramPacketB_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_sendLjava_net_DatagramPacketB(GOOD_IPv4);
     }
 
     public void test_sendLjava_net_DatagramPacketB_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_sendLjava_net_DatagramPacketB(GOOD_IPv6);
     }
 
@@ -531,6 +635,9 @@
     }
 
     public void test_setInterfaceLjava_net_InetAddress() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket();
         mss.setInterface(InetAddress.getLocalHost());
         InetAddress theInterface = mss.getInterface();
@@ -549,10 +656,16 @@
     }
 
     public void test_setInterface_unbound_address_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_setInterface_unbound_address(GOOD_IPv4);
     }
 
     public void test_setInterface_unbound_address_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_setInterface_unbound_address(GOOD_IPv6);
     }
 
@@ -568,6 +681,9 @@
     }
 
     public void test_setNetworkInterfaceLjava_net_NetworkInterface_null() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         // Validate that null interface is handled ok.
         MulticastSocket mss = new MulticastSocket();
         try {
@@ -579,6 +695,9 @@
     }
 
     public void test_setNetworkInterfaceLjava_net_NetworkInterface_round_trip() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         // Validate that we can get and set the interface.
         MulticastSocket mss = new MulticastSocket();
         mss.setNetworkInterface(ipv4NetworkInterface);
@@ -588,10 +707,16 @@
     }
 
     public void test_setNetworkInterfaceLjava_net_NetworkInterface_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_setNetworkInterfaceLjava_net_NetworkInterface(GOOD_IPv4);
     }
 
     public void test_setNetworkInterfaceLjava_net_NetworkInterface_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_setNetworkInterfaceLjava_net_NetworkInterface(GOOD_IPv6);
     }
 
@@ -601,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 =
@@ -630,6 +755,9 @@
     }
 
     public void test_setTimeToLiveI() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket();
         mss.setTimeToLive(120);
         assertEquals("Returned incorrect 1st TTL", 120, mss.getTimeToLive());
@@ -639,6 +767,9 @@
     }
 
     public void test_setTTLB() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket mss = new MulticastSocket();
         mss.setTTL((byte) 120);
         assertEquals("Failed to set TTL", 120, mss.getTTL());
@@ -646,6 +777,9 @@
     }
 
     public void test_ConstructorLjava_net_SocketAddress() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket ms = new MulticastSocket((SocketAddress) null);
         assertTrue("should not be bound", !ms.isBound() && !ms.isClosed() && !ms.isConnected());
         ms.bind(null);
@@ -677,6 +811,9 @@
     }
 
     public void test_getLoopbackMode() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket ms = new MulticastSocket(null);
         assertTrue("should not be bound", !ms.isBound() && !ms.isClosed() && !ms.isConnected());
         ms.getLoopbackMode();
@@ -686,6 +823,9 @@
     }
 
     public void test_setLoopbackModeZ() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         MulticastSocket ms = new MulticastSocket();
         ms.setLoopbackMode(true);
         assertTrue("loopback should be true", ms.getLoopbackMode());
@@ -696,10 +836,16 @@
     }
 
     public void test_setLoopbackModeSendReceive_IPv4() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_setLoopbackModeSendReceive(GOOD_IPv4);
     }
 
     public void test_setLoopbackModeSendReceive_IPv6() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         test_setLoopbackModeSendReceive(GOOD_IPv6);
     }
 
@@ -726,6 +872,9 @@
     }
 
     public void test_setReuseAddressZ() throws Exception {
+        if (!supportsMulticast) {
+            return;
+        }
         // Test case were we to set ReuseAddress to false.
         MulticastSocket theSocket1 = new MulticastSocket(null);
         theSocket1.setReuseAddress(false);
@@ -797,5 +946,4 @@
     private static String extractMessage(DatagramPacket rdp) {
         return new String(rdp.getData(), 0, rdp.getLength());
     }
-
 }
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/jar/JarFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
index f55829d..0bc8920 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
@@ -37,7 +37,14 @@
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Enumeration;
+import java.util.List;
 import java.util.Vector;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -97,6 +104,27 @@
 
     private final String emptyEntryJar = "EmptyEntries_signed.jar";
 
+    /*
+     * /usr/bin/openssl genrsa 2048 > root1.pem
+     * /usr/bin/openssl req -new -key root1.pem -out root1.csr -subj '/CN=root1'
+     * /usr/bin/openssl x509 -req -days 3650 -in root1.csr -signkey root1.pem -out root1.crt
+     * /usr/bin/openssl genrsa 2048 > root2.pem
+     * /usr/bin/openssl req -new -key root2.pem -out root2.csr -subj '/CN=root2'
+     * echo 4000 > root1.srl
+     * echo 8000 > root2.srl
+     * /usr/bin/openssl x509 -req -days 3650 -in root2.csr -CA root1.crt -CAkey root1.pem -out root2.crt
+     * /usr/bin/openssl x509 -req -days 3650 -in root1.csr -CA root2.crt -CAkey root2.pem -out root1.crt
+     * /usr/bin/openssl genrsa 2048 > signer.pem
+     * /usr/bin/openssl req -new -key signer.pem -out signer.csr -subj '/CN=signer'
+     * /usr/bin/openssl x509 -req -days 3650 -in signer.csr -CA root1.crt -CAkey root1.pem -out signer.crt
+     * /usr/bin/openssl pkcs12 -inkey signer.pem -in signer.crt -export -out signer.p12 -name signer -passout pass:certloop
+     * keytool -importkeystore -srckeystore signer.p12 -srcstoretype PKCS12 -destkeystore signer.jks -srcstorepass certloop -deststorepass certloop
+     * cat signer.crt root1.crt root2.crt > chain.crt
+     * zip -d hyts_certLoop.jar 'META-INF/*'
+     * jarsigner -keystore signer.jks -certchain chain.crt -storepass certloop hyts_certLoop.jar signer
+     */
+    private final String certLoopJar = "hyts_certLoop.jar";
+
     private final String emptyEntry1 = "subfolder/internalSubset01.js";
 
     private final String emptyEntry2 = "svgtest.js";
@@ -616,6 +644,9 @@
 
         // JAR with a signature that has PKCS#7 Authenticated Attributes
         checkSignedJar(authAttrsJar);
+
+        // JAR with certificates that loop
+        checkSignedJar(certLoopJar, 3);
     }
 
     /**
@@ -628,29 +659,52 @@
         checkSignedJar(jarName9);
     }
 
+    /**
+     * Checks that a JAR is signed correctly with a signature length of 1.
+     */
     private void checkSignedJar(String jarName) throws Exception {
+        checkSignedJar(jarName, 1);
+    }
+
+    /**
+     * Checks that a JAR is signed correctly with a signature length of sigLength.
+     */
+    private void checkSignedJar(String jarName, final int sigLength) throws Exception {
         Support_Resources.copyFile(resources, null, jarName);
 
-        File file = new File(resources, jarName);
-        boolean foundCerts = false;
+        final File file = new File(resources, jarName);
 
-        JarFile jarFile = new JarFile(file, true);
-        try {
-
-            Enumeration<JarEntry> e = jarFile.entries();
-            while (e.hasMoreElements()) {
-                JarEntry entry = e.nextElement();
-                InputStream is = jarFile.getInputStream(entry);
-                is.skip(100000);
-                is.close();
-                Certificate[] certs = entry.getCertificates();
-                if (certs != null && certs.length > 0) {
-                    foundCerts = true;
-                    break;
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Boolean> future = executor.submit(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                JarFile jarFile = new JarFile(file, true);
+                try {
+                    Enumeration<JarEntry> e = jarFile.entries();
+                    while (e.hasMoreElements()) {
+                        JarEntry entry = e.nextElement();
+                        InputStream is = jarFile.getInputStream(entry);
+                        is.skip(100000);
+                        is.close();
+                        Certificate[] certs = entry.getCertificates();
+                        if (certs != null && certs.length > 0) {
+                            assertEquals(sigLength, certs.length);
+                            return true;
+                        }
+                    }
+                    return false;
+                } finally {
+                    jarFile.close();
                 }
             }
-        } finally {
-            jarFile.close();
+        });
+        executor.shutdown();
+        final boolean foundCerts;
+        try {
+            foundCerts = future.get(10, TimeUnit.SECONDS);
+        } catch (TimeoutException e) {
+            fail("Could not finish building chain; possibly confused by loops");
+            return; // Not actually reached.
         }
 
         assertTrue(
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..4f0e036
--- /dev/null
+++ b/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.ibm.icu.** com.android.ibm.icu.@1
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/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java
index d766385..37b1d7d 100644
--- a/libart/src/main/java/java/lang/Class.java
+++ b/libart/src/main/java/java/lang/Class.java
@@ -55,6 +55,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;
@@ -881,18 +882,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 +892,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 +902,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
      * 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 +1012,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 +1028,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 +1052,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 +1062,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());
             }
         }
     }
diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java
index 76f4ea9..43066e1 100644
--- a/libart/src/main/java/java/lang/Daemons.java
+++ b/libart/src/main/java/java/lang/Daemons.java
@@ -57,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();
         }
@@ -127,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;
@@ -163,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
@@ -203,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();
@@ -305,6 +322,10 @@
     private static class HeapTaskDaemon extends Daemon {
         private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon();
 
+        HeapTaskDaemon() {
+            super("HeapTaskDaemon");
+        }
+
         // Overrides the Daemon.interupt method which is called from Daemons.stop.
         public synchronized void interrupt(Thread thread) {
             VMRuntime.getRuntime().stopHeapTaskProcessor();
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
index 6fdcdb2..1801862 100644
--- a/libart/src/main/java/java/lang/reflect/ArtField.java
+++ b/libart/src/main/java/java/lang/reflect/ArtField.java
@@ -51,46 +51,4 @@
      * 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/Constructor.java b/libart/src/main/java/java/lang/reflect/Constructor.java
index 2eb12b0..66ae143 100644
--- a/libart/src/main/java/java/lang/reflect/Constructor.java
+++ b/libart/src/main/java/java/lang/reflect/Constructor.java
@@ -330,4 +330,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..f0e4f5c 100644
--- a/libart/src/main/java/java/lang/reflect/Method.java
+++ b/libart/src/main/java/java/lang/reflect/Method.java
@@ -398,7 +398,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..e47b27b
--- a/libart/src/main/java/java/lang/reflect/Proxy.java
+++ b/libart/src/main/java/java/lang/reflect/Proxy.java
@@ -179,7 +179,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);
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/cacerts/04f60c28.0 b/luni/src/main/files/cacerts/04f60c28.0
new file mode 100644
index 0000000..dec2f4e
--- /dev/null
+++ b/luni/src/main/files/cacerts/04f60c28.0
@@ -0,0 +1,54 @@
+-----BEGIN CERTIFICATE-----
+MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
+eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
+JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
+MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
+Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
+VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
+I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
+o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
+A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
+zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
+RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            5c:8b:99:c5:5a:94:c5:d2:71:56:de:cd:89:80:cc:26
+    Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust ECC Certification Authority
+        Validity
+            Not Before: Feb  1 00:00:00 2010 GMT
+            Not After : Jan 18 23:59:59 2038 GMT
+        Subject: C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust ECC Certification Authority
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (384 bit)
+                pub: 
+                    04:1a:ac:54:5a:a9:f9:68:23:e7:7a:d5:24:6f:53:
+                    c6:5a:d8:4b:ab:c6:d5:b6:d1:e6:73:71:ae:dd:9c:
+                    d6:0c:61:fd:db:a0:89:03:b8:05:14:ec:57:ce:ee:
+                    5d:3f:e2:21:b3:ce:f7:d4:8a:79:e0:a3:83:7e:2d:
+                    97:d0:61:c4:f1:99:dc:25:91:63:ab:7f:30:a3:b4:
+                    70:e2:c7:a1:33:9c:f3:bf:2e:5c:53:b1:5f:b3:7d:
+                    32:7f:8a:34:e3:79:79
+                ASN1 OID: secp384r1
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                3A:E1:09:86:D4:CF:19:C2:96:76:74:49:76:DC:E0:35:C6:63:63:9A
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: ecdsa-with-SHA384
+         30:65:02:30:36:67:a1:16:08:dc:e4:97:00:41:1d:4e:be:e1:
+         63:01:cf:3b:aa:42:11:64:a0:9d:94:39:02:11:79:5c:7b:1d:
+         fa:64:b9:ee:16:42:b3:bf:8a:c2:09:c4:ec:e4:b1:4d:02:31:
+         00:e9:2a:61:47:8c:52:4a:4b:4e:18:70:f6:d6:44:d6:6e:f5:
+         83:ba:6d:58:bd:24:d9:56:48:ea:ef:c4:a2:46:81:88:6a:3a:
+         46:d1:a9:9b:4d:c9:61:da:d1:5d:57:6a:18
+SHA1 Fingerprint=D1:CB:CA:5D:B2:D5:2A:7F:69:3B:67:4D:E5:F0:5A:1D:0C:95:7D:F0
diff --git a/luni/src/main/files/cacerts/0d69c7e1.0 b/luni/src/main/files/cacerts/0d69c7e1.0
new file mode 100644
index 0000000..84c387e
--- /dev/null
+++ b/luni/src/main/files/cacerts/0d69c7e1.0
@@ -0,0 +1,47 @@
+-----BEGIN CERTIFICATE-----
+MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk
+MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH
+bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
+DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
+QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ
+FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F
+uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX
+kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs
+ewv4n4Q=
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            2a:38:a4:1c:96:0a:04:de:42:b2:28:a5:0b:e8:34:98:02
+    Signature Algorithm: ecdsa-with-SHA256
+        Issuer: OU=GlobalSign ECC Root CA - R4, O=GlobalSign, CN=GlobalSign
+        Validity
+            Not Before: Nov 13 00:00:00 2012 GMT
+            Not After : Jan 19 03:14:07 2038 GMT
+        Subject: OU=GlobalSign ECC Root CA - R4, O=GlobalSign, CN=GlobalSign
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (256 bit)
+                pub: 
+                    04:b8:c6:79:d3:8f:6c:25:0e:9f:2e:39:19:1c:03:
+                    a4:ae:9a:e5:39:07:09:16:ca:63:b1:b9:86:f8:8a:
+                    57:c1:57:ce:42:fa:73:a1:f7:65:42:ff:1e:c1:00:
+                    b2:6e:73:0e:ff:c7:21:e5:18:a4:aa:d9:71:3f:a8:
+                    d4:b9:ce:8c:1d
+                ASN1 OID: prime256v1
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Subject Key Identifier: 
+                54:B0:7B:AD:45:B8:E2:40:7F:FB:0A:6E:FB:BE:33:C9:3C:A3:84:D5
+    Signature Algorithm: ecdsa-with-SHA256
+         30:45:02:21:00:dc:92:a1:a0:13:a6:cf:03:b0:e6:c4:21:97:
+         90:fa:14:57:2d:03:ec:ee:3c:d3:6e:ca:a8:6c:76:bc:a2:de:
+         bb:02:20:27:a8:85:27:35:9b:56:c6:a3:f2:47:d2:b7:6e:1b:
+         02:00:17:aa:67:a6:15:91:de:fa:94:ec:7b:0b:f8:9f:84
+SHA1 Fingerprint=69:69:56:2E:40:80:F4:24:A1:E7:19:9F:14:BA:F3:EE:58:AB:6A:BB
diff --git a/luni/src/main/files/cacerts/2add47b6.0 b/luni/src/main/files/cacerts/2add47b6.0
new file mode 100644
index 0000000..aa88539
--- /dev/null
+++ b/luni/src/main/files/cacerts/2add47b6.0
@@ -0,0 +1,52 @@
+-----BEGIN CERTIFICATE-----
+MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
+MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
+bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
+DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
+QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc
+8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke
+hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
+VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI
+KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
+515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO
+xwy8p2Fp8fc74SrL+SvzZpA3
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            60:59:49:e0:26:2e:bb:55:f9:0a:77:8a:71:f9:4a:d8:6c
+    Signature Algorithm: ecdsa-with-SHA384
+        Issuer: OU=GlobalSign ECC Root CA - R5, O=GlobalSign, CN=GlobalSign
+        Validity
+            Not Before: Nov 13 00:00:00 2012 GMT
+            Not After : Jan 19 03:14:07 2038 GMT
+        Subject: OU=GlobalSign ECC Root CA - R5, O=GlobalSign, CN=GlobalSign
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (384 bit)
+                pub: 
+                    04:47:45:0e:96:fb:7d:5d:bf:e9:39:d1:21:f8:9f:
+                    0b:b6:d5:7b:1e:92:3a:48:59:1c:f0:62:31:2d:c0:
+                    7a:28:fe:1a:a7:5c:b3:b6:cc:97:e7:45:d4:58:fa:
+                    d1:77:6d:43:a2:c0:87:65:34:0a:1f:7a:dd:eb:3c:
+                    33:a1:c5:9d:4d:a4:6f:41:95:38:7f:c9:1e:84:eb:
+                    d1:9e:49:92:87:94:87:0c:3a:85:4a:66:9f:9d:59:
+                    93:4d:97:61:06:86:4a
+                ASN1 OID: secp384r1
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Subject Key Identifier: 
+                3D:E6:29:48:9B:EA:07:CA:21:44:4A:26:DE:6E:DE:D2:83:D0:9F:59
+    Signature Algorithm: ecdsa-with-SHA384
+         30:65:02:31:00:e5:69:12:c9:6e:db:c6:31:ba:09:41:e1:97:
+         f8:fb:fd:9a:e2:7d:12:c9:ed:7c:64:d3:cb:05:25:8b:56:d9:
+         a0:e7:5e:5d:4e:0b:83:9c:5b:76:29:a0:09:26:21:6a:62:02:
+         30:71:d2:b5:8f:5c:ea:3b:e1:78:09:85:a8:75:92:3b:c8:5c:
+         fd:48:ef:0d:74:22:a8:08:e2:6e:c5:49:ce:c7:0c:bc:a7:61:
+         69:f1:f7:3b:e1:2a:cb:f9:2b:f3:66:90:37
+SHA1 Fingerprint=1F:24:C6:30:CD:A4:18:EF:20:69:FF:AD:4F:DD:5F:46:3A:1B:69:AA
diff --git a/luni/src/main/files/cacerts/35105088.0 b/luni/src/main/files/cacerts/35105088.0
new file mode 100644
index 0000000..3728c3b
--- /dev/null
+++ b/luni/src/main/files/cacerts/35105088.0
@@ -0,0 +1,123 @@
+-----BEGIN CERTIFICATE-----
+MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
+iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
+cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
+BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
+MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
+BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
+3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
+tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
+Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
+VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
+79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
+c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
+Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
+c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
+UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
+Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
+BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
+Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
+VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
+ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
+8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
+iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
+Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
+XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
+qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
+VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
+L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
+jjxDah2nGN59PRbxYvnKkKj9
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            01:fd:6d:30:fc:a3:ca:51:a8:1b:bc:64:0e:35:03:2d
+    Signature Algorithm: sha384WithRSAEncryption
+        Issuer: C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority
+        Validity
+            Not Before: Feb  1 00:00:00 2010 GMT
+            Not After : Jan 18 23:59:59 2038 GMT
+        Subject: C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:80:12:65:17:36:0e:c3:db:08:b3:d0:ac:57:0d:
+                    76:ed:cd:27:d3:4c:ad:50:83:61:e2:aa:20:4d:09:
+                    2d:64:09:dc:ce:89:9f:cc:3d:a9:ec:f6:cf:c1:dc:
+                    f1:d3:b1:d6:7b:37:28:11:2b:47:da:39:c6:bc:3a:
+                    19:b4:5f:a6:bd:7d:9d:a3:63:42:b6:76:f2:a9:3b:
+                    2b:91:f8:e2:6f:d0:ec:16:20:90:09:3e:e2:e8:74:
+                    c9:18:b4:91:d4:62:64:db:7f:a3:06:f1:88:18:6a:
+                    90:22:3c:bc:fe:13:f0:87:14:7b:f6:e4:1f:8e:d4:
+                    e4:51:c6:11:67:46:08:51:cb:86:14:54:3f:bc:33:
+                    fe:7e:6c:9c:ff:16:9d:18:bd:51:8e:35:a6:a7:66:
+                    c8:72:67:db:21:66:b1:d4:9b:78:03:c0:50:3a:e8:
+                    cc:f0:dc:bc:9e:4c:fe:af:05:96:35:1f:57:5a:b7:
+                    ff:ce:f9:3d:b7:2c:b6:f6:54:dd:c8:e7:12:3a:4d:
+                    ae:4c:8a:b7:5c:9a:b4:b7:20:3d:ca:7f:22:34:ae:
+                    7e:3b:68:66:01:44:e7:01:4e:46:53:9b:33:60:f7:
+                    94:be:53:37:90:73:43:f3:32:c3:53:ef:db:aa:fe:
+                    74:4e:69:c7:6b:8c:60:93:de:c4:c7:0c:df:e1:32:
+                    ae:cc:93:3b:51:78:95:67:8b:ee:3d:56:fe:0c:d0:
+                    69:0f:1b:0f:f3:25:26:6b:33:6d:f7:6e:47:fa:73:
+                    43:e5:7e:0e:a5:66:b1:29:7c:32:84:63:55:89:c4:
+                    0d:c1:93:54:30:19:13:ac:d3:7d:37:a7:eb:5d:3a:
+                    6c:35:5c:db:41:d7:12:da:a9:49:0b:df:d8:80:8a:
+                    09:93:62:8e:b5:66:cf:25:88:cd:84:b8:b1:3f:a4:
+                    39:0f:d9:02:9e:eb:12:4c:95:7c:f3:6b:05:a9:5e:
+                    16:83:cc:b8:67:e2:e8:13:9d:cc:5b:82:d3:4c:b3:
+                    ed:5b:ff:de:e5:73:ac:23:3b:2d:00:bf:35:55:74:
+                    09:49:d8:49:58:1a:7f:92:36:e6:51:92:0e:f3:26:
+                    7d:1c:4d:17:bc:c9:ec:43:26:d0:bf:41:5f:40:a9:
+                    44:44:f4:99:e7:57:87:9e:50:1f:57:54:a8:3e:fd:
+                    74:63:2f:b1:50:65:09:e6:58:42:2e:43:1a:4c:b4:
+                    f0:25:47:59:fa:04:1e:93:d4:26:46:4a:50:81:b2:
+                    de:be:78:b7:fc:67:15:e1:c9:57:84:1e:0f:63:d6:
+                    e9:62:ba:d6:5f:55:2e:ea:5c:c6:28:08:04:25:39:
+                    b8:0e:2b:a9:f2:4c:97:1c:07:3f:0d:52:f5:ed:ef:
+                    2f:82:0f
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                53:79:BF:5A:AA:2B:4A:CF:54:80:E1:D8:9B:C0:9D:F2:B2:03:66:CB
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha384WithRSAEncryption
+         5c:d4:7c:0d:cf:f7:01:7d:41:99:65:0c:73:c5:52:9f:cb:f8:
+         cf:99:06:7f:1b:da:43:15:9f:9e:02:55:57:96:14:f1:52:3c:
+         27:87:94:28:ed:1f:3a:01:37:a2:76:fc:53:50:c0:84:9b:c6:
+         6b:4e:ba:8c:21:4f:a2:8e:55:62:91:f3:69:15:d8:bc:88:e3:
+         c4:aa:0b:fd:ef:a8:e9:4b:55:2a:06:20:6d:55:78:29:19:ee:
+         5f:30:5c:4b:24:11:55:ff:24:9a:6e:5e:2a:2b:ee:0b:4d:9f:
+         7f:f7:01:38:94:14:95:43:07:09:fb:60:a9:ee:1c:ab:12:8c:
+         a0:9a:5e:a7:98:6a:59:6d:8b:3f:08:fb:c8:d1:45:af:18:15:
+         64:90:12:0f:73:28:2e:c5:e2:24:4e:fc:58:ec:f0:f4:45:fe:
+         22:b3:eb:2f:8e:d2:d9:45:61:05:c1:97:6f:a8:76:72:8f:8b:
+         8c:36:af:bf:0d:05:ce:71:8d:e6:a6:6f:1f:6c:a6:71:62:c5:
+         d8:d0:83:72:0c:f1:67:11:89:0c:9c:13:4c:72:34:df:bc:d5:
+         71:df:aa:71:dd:e1:b9:6c:8c:3c:12:5d:65:da:bd:57:12:b6:
+         43:6b:ff:e5:de:4d:66:11:51:cf:99:ae:ec:17:b6:e8:71:91:
+         8c:de:49:fe:dd:35:71:a2:15:27:94:1c:cf:61:e3:26:bb:6f:
+         a3:67:25:21:5d:e6:dd:1d:0b:2e:68:1b:3b:82:af:ec:83:67:
+         85:d4:98:51:74:b1:b9:99:80:89:ff:7f:78:19:5c:79:4a:60:
+         2e:92:40:ae:4c:37:2a:2c:c9:c7:62:c8:0e:5d:f7:36:5b:ca:
+         e0:25:25:01:b4:dd:1a:07:9c:77:00:3f:d0:dc:d5:ec:3d:d4:
+         fa:bb:3f:cc:85:d6:6f:7f:a9:2d:df:b9:02:f7:f5:97:9a:b5:
+         35:da:c3:67:b0:87:4a:a9:28:9e:23:8e:ff:5c:27:6b:e1:b0:
+         4f:f3:07:ee:00:2e:d4:59:87:cb:52:41:95:ea:f4:47:d7:ee:
+         64:41:55:7c:8d:59:02:95:dd:62:9d:c2:b9:ee:5a:28:74:84:
+         a5:9b:b7:90:c7:0c:07:df:f5:89:36:74:32:d6:28:c1:b0:b0:
+         0b:e0:9c:4c:c3:1c:d6:fc:e3:69:b5:47:46:81:2f:a2:82:ab:
+         d3:63:44:70:c4:8d:ff:2d:33:ba:ad:8f:7b:b5:70:88:ae:3e:
+         19:cf:40:28:d8:fc:c8:90:bb:5d:99:22:f5:52:e6:58:c5:1f:
+         88:31:43:ee:88:1d:d7:c6:8e:3c:43:6a:1d:a7:18:de:7d:3d:
+         16:f1:62:f9:ca:90:a8:fd
+SHA1 Fingerprint=2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E
diff --git a/luni/src/main/files/cacerts/d4c339cb.0 b/luni/src/main/files/cacerts/d4c339cb.0
new file mode 100644
index 0000000..3f5e924
--- /dev/null
+++ b/luni/src/main/files/cacerts/d4c339cb.0
@@ -0,0 +1,123 @@
+-----BEGIN CERTIFICATE-----
+MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
+hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
+BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
+MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
+EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
+6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
+pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
+9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
+/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
+Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
+qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
+SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
+u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
+Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
+crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
+FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
+/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
+wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
+4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
+2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
+FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
+CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
+boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
+jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
+S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
+QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
+0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
+NVOFBkpdn627G190
+-----END CERTIFICATE-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            4c:aa:f9:ca:db:63:6f:e0:1f:f7:4e:d8:5b:03:86:9d
+    Signature Algorithm: sha384WithRSAEncryption
+        Issuer: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO RSA Certification Authority
+        Validity
+            Not Before: Jan 19 00:00:00 2010 GMT
+            Not After : Jan 18 23:59:59 2038 GMT
+        Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO RSA Certification Authority
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (4096 bit)
+                Modulus:
+                    00:91:e8:54:92:d2:0a:56:b1:ac:0d:24:dd:c5:cf:
+                    44:67:74:99:2b:37:a3:7d:23:70:00:71:bc:53:df:
+                    c4:fa:2a:12:8f:4b:7f:10:56:bd:9f:70:72:b7:61:
+                    7f:c9:4b:0f:17:a7:3d:e3:b0:04:61:ee:ff:11:97:
+                    c7:f4:86:3e:0a:fa:3e:5c:f9:93:e6:34:7a:d9:14:
+                    6b:e7:9c:b3:85:a0:82:7a:76:af:71:90:d7:ec:fd:
+                    0d:fa:9c:6c:fa:df:b0:82:f4:14:7e:f9:be:c4:a6:
+                    2f:4f:7f:99:7f:b5:fc:67:43:72:bd:0c:00:d6:89:
+                    eb:6b:2c:d3:ed:8f:98:1c:14:ab:7e:e5:e3:6e:fc:
+                    d8:a8:e4:92:24:da:43:6b:62:b8:55:fd:ea:c1:bc:
+                    6c:b6:8b:f3:0e:8d:9a:e4:9b:6c:69:99:f8:78:48:
+                    30:45:d5:ad:e1:0d:3c:45:60:fc:32:96:51:27:bc:
+                    67:c3:ca:2e:b6:6b:ea:46:c7:c7:20:a0:b1:1f:65:
+                    de:48:08:ba:a4:4e:a9:f2:83:46:37:84:eb:e8:cc:
+                    81:48:43:67:4e:72:2a:9b:5c:bd:4c:1b:28:8a:5c:
+                    22:7b:b4:ab:98:d9:ee:e0:51:83:c3:09:46:4e:6d:
+                    3e:99:fa:95:17:da:7c:33:57:41:3c:8d:51:ed:0b:
+                    b6:5c:af:2c:63:1a:df:57:c8:3f:bc:e9:5d:c4:9b:
+                    af:45:99:e2:a3:5a:24:b4:ba:a9:56:3d:cf:6f:aa:
+                    ff:49:58:be:f0:a8:ff:f4:b8:ad:e9:37:fb:ba:b8:
+                    f4:0b:3a:f9:e8:43:42:1e:89:d8:84:cb:13:f1:d9:
+                    bb:e1:89:60:b8:8c:28:56:ac:14:1d:9c:0a:e7:71:
+                    eb:cf:0e:dd:3d:a9:96:a1:48:bd:3c:f7:af:b5:0d:
+                    22:4c:c0:11:81:ec:56:3b:f6:d3:a2:e2:5b:b7:b2:
+                    04:22:52:95:80:93:69:e8:8e:4c:65:f1:91:03:2d:
+                    70:74:02:ea:8b:67:15:29:69:52:02:bb:d7:df:50:
+                    6a:55:46:bf:a0:a3:28:61:7f:70:d0:c3:a2:aa:2c:
+                    21:aa:47:ce:28:9c:06:45:76:bf:82:18:27:b4:d5:
+                    ae:b4:cb:50:e6:6b:f4:4c:86:71:30:e9:a6:df:16:
+                    86:e0:d8:ff:40:dd:fb:d0:42:88:7f:a3:33:3a:2e:
+                    5c:1e:41:11:81:63:ce:18:71:6b:2b:ec:a6:8a:b7:
+                    31:5c:3a:6a:47:e0:c3:79:59:d6:20:1a:af:f2:6a:
+                    98:aa:72:bc:57:4a:d2:4b:9d:bb:10:fc:b0:4c:41:
+                    e5:ed:1d:3d:5e:28:9d:9c:cc:bf:b3:51:da:a7:47:
+                    e5:84:53
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha384WithRSAEncryption
+         0a:f1:d5:46:84:b7:ae:51:bb:6c:b2:4d:41:14:00:93:4c:9c:
+         cb:e5:c0:54:cf:a0:25:8e:02:f9:fd:b0:a2:0d:f5:20:98:3c:
+         13:2d:ac:56:a2:b0:d6:7e:11:92:e9:2e:ba:9e:2e:9a:72:b1:
+         bd:19:44:6c:61:35:a2:9a:b4:16:12:69:5a:8c:e1:d7:3e:a4:
+         1a:e8:2f:03:f4:ae:61:1d:10:1b:2a:a4:8b:7a:c5:fe:05:a6:
+         e1:c0:d6:c8:fe:9e:ae:8f:2b:ba:3d:99:f8:d8:73:09:58:46:
+         6e:a6:9c:f4:d7:27:d3:95:da:37:83:72:1c:d3:73:e0:a2:47:
+         99:03:38:5d:d5:49:79:00:29:1c:c7:ec:9b:20:1c:07:24:69:
+         57:78:b2:39:fc:3a:84:a0:b5:9c:7c:8d:bf:2e:93:62:27:b7:
+         39:da:17:18:ae:bd:3c:09:68:ff:84:9b:3c:d5:d6:0b:03:e3:
+         57:9e:14:f7:d1:eb:4f:c8:bd:87:23:b7:b6:49:43:79:85:5c:
+         ba:eb:92:0b:a1:c6:e8:68:a8:4c:16:b1:1a:99:0a:e8:53:2c:
+         92:bb:a1:09:18:75:0c:65:a8:7b:cb:23:b7:1a:c2:28:85:c3:
+         1b:ff:d0:2b:62:ef:a4:7b:09:91:98:67:8c:14:01:cd:68:06:
+         6a:63:21:75:03:80:88:8a:6e:81:c6:85:f2:a9:a4:2d:e7:f4:
+         a5:24:10:47:83:ca:cd:f4:8d:79:58:b1:06:9b:e7:1a:2a:d9:
+         9d:01:d7:94:7d:ed:03:4a:ca:f0:db:e8:a9:01:3e:f5:56:99:
+         c9:1e:8e:49:3d:bb:e5:09:b9:e0:4f:49:92:3d:16:82:40:cc:
+         cc:59:c6:e6:3a:ed:12:2e:69:3c:6c:95:b1:fd:aa:1d:7b:7f:
+         86:be:1e:0e:32:46:fb:fb:13:8f:75:7f:4c:8b:4b:46:63:fe:
+         00:34:40:70:c1:c3:b9:a1:dd:a6:70:e2:04:b3:41:bc:e9:80:
+         91:ea:64:9c:7a:e1:22:03:a9:9c:6e:6f:0e:65:4f:6c:87:87:
+         5e:f3:6e:a0:f9:75:a5:9b:40:e8:53:b2:27:9d:4a:b9:c0:77:
+         21:8d:ff:87:f2:de:bc:8c:ef:17:df:b7:49:0b:d1:f2:6e:30:
+         0b:1a:0e:4e:76:ed:11:fc:f5:e9:56:b2:7d:bf:c7:6d:0a:93:
+         8c:a5:d0:c0:b6:1d:be:3a:4e:94:a2:d7:6e:6c:0b:c2:8a:7c:
+         fa:20:f3:c4:e4:e5:cd:0d:a8:cb:91:92:b1:7c:85:ec:b5:14:
+         69:66:0e:82:e7:cd:ce:c8:2d:a6:51:7f:21:c1:35:53:85:06:
+         4a:5d:9f:ad:bb:1b:5f:74
+SHA1 Fingerprint=AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4
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 8028f23..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>.
    */
@@ -406,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); }
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 55870ec..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();
@@ -365,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();
@@ -434,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/lang/Math.java b/luni/src/main/java/java/lang/Math.java
index a738511..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 final Random INSTANCE = new Random();
+    private static class NoImagePreloadHolder {
+        private static final Random INSTANCE = new Random();
+    }
 
     /**
      * Prevents this class from being instantiated.
@@ -876,7 +878,7 @@
      * @return a pseudo-random number.
      */
     public static double random() {
-        return INSTANCE.nextDouble();
+        return NoImagePreloadHolder.INSTANCE.nextDouble();
     }
 
     /**
@@ -886,14 +888,14 @@
      * @hide for internal use only.
      */
     public static void setRandomSeedInternal(long seed) {
-        INSTANCE.setSeed(seed);
+        NoImagePreloadHolder.INSTANCE.setSeed(seed);
     }
 
     /**
      * @hide for internal use only.
      */
     public static int randomIntInternal() {
-        return INSTANCE.nextInt();
+        return NoImagePreloadHolder.INSTANCE.nextInt();
     }
 
     /**
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/System.java b/luni/src/main/java/java/lang/System.java
index 8e531c3..1ccda16 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -797,6 +797,12 @@
         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.
+        p.put("com.android.ibm.icu.impl.ICUBinary.dataPath", getenv("ANDROID_ROOT") + "/usr/icu");
+
         parsePropertyAssignments(p, specialProperties());
 
         // Override built-in properties with settings from the command line.
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/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
index 4415498..9d53328 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
@@ -442,26 +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.
      */
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/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index b1a056a..1885f83 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -95,7 +95,7 @@
  *     <td><a href="http://site.icu-project.org/download/51">ICU 51</a></td>
  *     <td><a href="http://cldr.unicode.org/index/downloads/cldr-23">CLDR 23</a></td>
  *     <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
- * <tr><td>Android 4.? (STOPSHIP)</td>
+ * <tr><td>Android 5.0 (Lollipop)</td>
  *     <td><a href="http://site.icu-project.org/download/53">ICU 53</a></td>
  *     <td><a href="http://cldr.unicode.org/index/downloads/cldr-25">CLDR 25</a></td>
  *     <td><a href="http://www.unicode.org/versions/Unicode6.3.0/">Unicode 6.3</a></td></tr>
@@ -278,11 +278,6 @@
      */
     private static final TreeMap<String, String> GRANDFATHERED_LOCALES;
 
-    /**
-     * The default locale, returned by {@code Locale.getDefault()}.
-     */
-    private static Locale defaultLocale;
-
     static {
         GRANDFATHERED_LOCALES = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
 
@@ -319,9 +314,14 @@
         GRANDFATHERED_LOCALES.put("zh-min", "nan-x-zh-min");
         GRANDFATHERED_LOCALES.put("zh-min-nan", "nan");
         GRANDFATHERED_LOCALES.put("zh-xiang", "hsn");
+    }
 
-        // Initialize the default locale from the system properties.
-        defaultLocale = getDefaultLocaleFromSystemProperties();
+    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();
     }
 
     /**
@@ -1066,7 +1066,7 @@
      * Instead, use this method to look it up for each use.
      */
     public static Locale getDefault() {
-        return defaultLocale;
+        return NoImagePreloadHolder.defaultLocale;
     }
 
     /**
@@ -1660,7 +1660,7 @@
             throw new NullPointerException("locale == null");
         }
         String languageTag = locale.toLanguageTag();
-        defaultLocale = locale;
+        NoImagePreloadHolder.defaultLocale = locale;
         ICU.setDefaultLocale(languageTag);
     }
 
diff --git a/luni/src/main/java/java/util/logging/SocketHandler.java b/luni/src/main/java/java/util/logging/SocketHandler.java
index c6d4be0..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;
@@ -118,6 +119,9 @@
         }
         // 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/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 771af83..26f6863 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -268,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;
     }
 
     /**
@@ -380,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,
@@ -439,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 889ac9f..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) {
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/SSLEngine.java b/luni/src/main/java/javax/net/ssl/SSLEngine.java
index 7da8f1f..f40f4b0 100644
--- a/luni/src/main/java/javax/net/ssl/SSLEngine.java
+++ b/luni/src/main/java/javax/net/ssl/SSLEngine.java
@@ -62,7 +62,6 @@
  * </table>
  *
  * <h4>Cipher suites</h4>
- * <!-- STOPSHIP: Update this table to reflect the removal of DSS cipher suites. -->
  * <table>
  *     <thead>
  *         <tr>
@@ -74,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>
@@ -179,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 043b005..c6906c5 100644
--- a/luni/src/main/java/javax/net/ssl/SSLSocket.java
+++ b/luni/src/main/java/javax/net/ssl/SSLSocket.java
@@ -106,7 +106,6 @@
  * standard names for cipher suites since API Level 9, as listed in the table
  * below. Prior to API Level 9, non-standard (OpenSSL) names had been used (see
  * the table following this table).
- * <!-- STOPSHIP: Update this table to reflect the removal of DSS cipher suites. -->
  * <table>
  *     <thead>
  *         <tr>
@@ -118,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>
@@ -213,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>
@@ -640,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>
@@ -664,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>
@@ -694,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/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/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/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
index 0e715b6..e861571 100644
--- a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
+++ b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
@@ -32,6 +32,7 @@
   // match the ones in DateUtils.java.
   public static final int FORMAT_SHOW_TIME = 0x00001;
   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_ABBREV_MONTH = 0x10000;
   public static final int FORMAT_NUMERIC_DATE = 0x20000;
@@ -222,6 +223,27 @@
     } else {
       // 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 timeCalendar = new GregorianCalendar(false);
+          timeCalendar.setTimeZone(tz);
+          timeCalendar.setTimeInMillis(time);
+          Calendar nowCalendar = new GregorianCalendar(false);
+          nowCalendar.setTimeZone(tz);
+          nowCalendar.setTimeInMillis(now);
+
+          if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+              flags |= FORMAT_SHOW_YEAR;
+          } else {
+              flags |= FORMAT_NO_YEAR;
+          }
+      }
+
       return DateIntervalFormat.formatDateRange(locale, tz, time, time, flags);
     }
 
@@ -323,15 +345,12 @@
     } else {
       // We always use fixed flags to format the date clause. User-supplied
       // flags are ignored.
-      if (days == 0) {
-        // Same day
-        flags = FORMAT_SHOW_TIME;
-      } else if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+      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_ABBREV_MONTH;
+        flags = FORMAT_SHOW_DATE | FORMAT_NO_YEAR | FORMAT_ABBREV_MONTH;
       }
 
       dateClause = DateIntervalFormat.formatDateRange(locale, tz, time, time, flags);
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 5c90757..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(); }
@@ -133,6 +135,7 @@
     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); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 620af19..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();
@@ -125,6 +127,7 @@
     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;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 2629553..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();
@@ -206,7 +208,11 @@
         // 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;
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/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/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/org/apache/harmony/security/utils/JarUtils.java b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
index 33584d8..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
@@ -31,6 +31,7 @@
 import java.security.Principal;
 import java.security.Signature;
 import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -82,8 +83,10 @@
         CertificateFactory cf = CertificateFactory.getInstance("X.509");
         int i = 0;
         for (org.apache.harmony.security.x509.Certificate encCert : encCerts) {
-            final InputStream is = new ByteArrayInputStream(encCert.getEncoded());
-            certs[i++] = (X509Certificate) cf.generateCertificate(is);
+            final byte[] encoded = encCert.getEncoded();
+            final InputStream is = new ByteArrayInputStream(encoded);
+            certs[i++] = new VerbatimX509Certificate((X509Certificate) cf.generateCertificate(is),
+                    encoded);
         }
 
         List<SignerInfo> sigInfos = signedData.getSignerInfos();
@@ -246,6 +249,10 @@
             }
             chain.add(issuerCert);
             count++;
+            /* Prevent growing infinitely if there is a loop */
+            if (count > candidates.length) {
+                break;
+            }
             issuer = issuerCert.getIssuerDN();
             if (issuerCert.getSubjectDN().equals(issuer)) {
                 break;
@@ -263,4 +270,22 @@
         return null;
     }
 
+    /**
+     * For legacy reasons we need to return exactly the original encoded
+     * certificate bytes, instead of letting the underlying implementation have
+     * a shot at re-encoding the data.
+     */
+    private static class VerbatimX509Certificate extends WrappedX509Certificate {
+        private byte[] encodedVerbatim;
+
+        public VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) {
+            super(wrapped);
+            this.encodedVerbatim = encodedVerbatim;
+        }
+
+        @Override
+        public byte[] getEncoded() throws CertificateEncodingException {
+            return encodedVerbatim;
+        }
+    }
 }
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
new file mode 100644
index 0000000..1c07915
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
@@ -0,0 +1,175 @@
+/*
+ * 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 org.apache.harmony.security.utils;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+public class WrappedX509Certificate extends X509Certificate {
+    private final X509Certificate wrapped;
+
+    public WrappedX509Certificate(X509Certificate wrapped) {
+        this.wrapped = wrapped;
+    }
+
+    @Override
+    public Set<String> getCriticalExtensionOIDs() {
+        return wrapped.getCriticalExtensionOIDs();
+    }
+
+    @Override
+    public byte[] getExtensionValue(String oid) {
+        return wrapped.getExtensionValue(oid);
+    }
+
+    @Override
+    public Set<String> getNonCriticalExtensionOIDs() {
+        return wrapped.getNonCriticalExtensionOIDs();
+    }
+
+    @Override
+    public boolean hasUnsupportedCriticalExtension() {
+        return wrapped.hasUnsupportedCriticalExtension();
+    }
+
+    @Override
+    public void checkValidity() throws CertificateExpiredException,
+            CertificateNotYetValidException {
+        wrapped.checkValidity();
+    }
+
+    @Override
+    public void checkValidity(Date date) throws CertificateExpiredException,
+            CertificateNotYetValidException {
+        wrapped.checkValidity(date);
+    }
+
+    @Override
+    public int getVersion() {
+        return wrapped.getVersion();
+    }
+
+    @Override
+    public BigInteger getSerialNumber() {
+        return wrapped.getSerialNumber();
+    }
+
+    @Override
+    public Principal getIssuerDN() {
+        return wrapped.getIssuerDN();
+    }
+
+    @Override
+    public Principal getSubjectDN() {
+        return wrapped.getSubjectDN();
+    }
+
+    @Override
+    public Date getNotBefore() {
+        return wrapped.getNotBefore();
+    }
+
+    @Override
+    public Date getNotAfter() {
+        return wrapped.getNotAfter();
+    }
+
+    @Override
+    public byte[] getTBSCertificate() throws CertificateEncodingException {
+        return wrapped.getTBSCertificate();
+    }
+
+    @Override
+    public byte[] getSignature() {
+        return wrapped.getSignature();
+    }
+
+    @Override
+    public String getSigAlgName() {
+        return wrapped.getSigAlgName();
+    }
+
+    @Override
+    public String getSigAlgOID() {
+        return wrapped.getSigAlgOID();
+    }
+
+    @Override
+    public byte[] getSigAlgParams() {
+        return wrapped.getSigAlgParams();
+    }
+
+    @Override
+    public boolean[] getIssuerUniqueID() {
+        return wrapped.getIssuerUniqueID();
+    }
+
+    @Override
+    public boolean[] getSubjectUniqueID() {
+        return wrapped.getSubjectUniqueID();
+    }
+
+    @Override
+    public boolean[] getKeyUsage() {
+        return wrapped.getKeyUsage();
+    }
+
+    @Override
+    public int getBasicConstraints() {
+        return wrapped.getBasicConstraints();
+    }
+
+    @Override
+    public byte[] getEncoded() throws CertificateEncodingException {
+        return wrapped.getEncoded();
+    }
+
+    @Override
+    public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
+            InvalidKeyException, NoSuchProviderException, SignatureException {
+        wrapped.verify(key);
+    }
+
+    @Override
+    public void verify(PublicKey key, String sigProvider) throws CertificateException,
+            NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
+            SignatureException {
+        wrapped.verify(key, sigProvider);
+    }
+
+    @Override
+    public String toString() {
+        return wrapped.toString();
+    }
+
+    @Override
+    public PublicKey getPublicKey() {
+        return wrapped.getPublicKey();
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
index d1079c8..040a012 100644
--- a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
@@ -416,11 +416,13 @@
 
     private String resolveCharacterReference(String value, int base) {
         try {
-            int ch = Integer.parseInt(value, base);
-            if (ch < 0 || ch > Character.MAX_VALUE) {
-                return null;
+            int codePoint = Integer.parseInt(value, base);
+            if (Character.isBmpCodePoint(codePoint)) {
+                return String.valueOf((char) codePoint);
+            } else {
+                char[] surrogatePair = Character.toChars(codePoint);
+                return new String(surrogatePair);
             }
-            return String.valueOf((char) ch);
         } catch (NumberFormatException ex) {
             return null;
         }
diff --git a/luni/src/main/native/IcuUtilities.cpp b/luni/src/main/native/IcuUtilities.cpp
index b740719..98648a5 100644
--- a/luni/src/main/native/IcuUtilities.cpp
+++ b/luni/src/main/native/IcuUtilities.cpp
@@ -27,7 +27,7 @@
 #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;
   }
@@ -39,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 bf6a8ee..c64de30 100644
--- a/luni/src/main/native/IcuUtilities.h
+++ b/luni/src/main/native/IcuUtilities.h
@@ -21,7 +21,7 @@
 #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/Register.cpp b/luni/src/main/native/Register.cpp
index d8c9d5c..fdc4027 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -60,7 +60,6 @@
     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);
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index f00c922..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);
@@ -407,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);
@@ -491,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_StringToReal.cpp b/luni/src/main/native/java_lang_StringToReal.cpp
old mode 100755
new mode 100644
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
index f2f1bf3..35d014c 100644
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ b/luni/src/main/native/java_util_regex_Matcher.cpp
@@ -29,8 +29,8 @@
 
 // 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));
 }
 
 /**
@@ -74,7 +74,7 @@
         maybeThrowIcuException(mEnv, "utext_close", mStatus);
     }
 
-    RegexMatcher* operator->() {
+    icu::RegexMatcher* operator->() {
         return mMatcher;
     }
 
@@ -106,7 +106,7 @@
 
     JNIEnv* mEnv;
     jstring mJavaInput;
-    RegexMatcher* mMatcher;
+    icu::RegexMatcher* mMatcher;
     const jchar* mChars;
     UErrorCode mStatus;
     UText* mUText;
@@ -170,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/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
index 5221baa..f369a09 100644
--- a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
@@ -35,7 +35,7 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  DateIntervalFormat* formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), icuLocale.locale(), status));
+  icu::DateIntervalFormat* formatter(icu::DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), icuLocale.locale(), status));
   if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) {
     return 0;
   }
@@ -44,21 +44,21 @@
   if (!tzNameHolder.valid()) {
     return 0;
   }
-  formatter->adoptTimeZone(TimeZone::createTimeZone(tzNameHolder.unicodeString()));
+  formatter->adoptTimeZone(icu::TimeZone::createTimeZone(tzNameHolder.unicodeString()));
 
   return reinterpret_cast<uintptr_t>(formatter);
 }
 
 static void DateIntervalFormat_destroyDateIntervalFormat(JNIEnv*, jclass, jlong address) {
-  delete reinterpret_cast<DateIntervalFormat*>(address);
+  delete reinterpret_cast<icu::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);
+  icu::DateIntervalFormat* formatter(reinterpret_cast<icu::DateIntervalFormat*>(address));
+  icu::DateInterval date_interval(fromDate, toDate);
 
-  UnicodeString s;
-  FieldPosition pos = 0;
+  icu::UnicodeString s;
+  icu::FieldPosition pos = 0;
   UErrorCode status = U_ZERO_ERROR;
   formatter->format(&date_interval, s, pos, status);
   if (maybeThrowIcuException(env, "DateIntervalFormat::format", status)) {
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 221d1b3..0e744b7 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -38,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"
@@ -114,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);
 }
@@ -124,7 +125,7 @@
   if (!currencyCode.valid()) {
     return 0;
   }
-  UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+  icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
   return ucurr_getNumericCode(icuCurrencyCode.getTerminatedBuffer());
 }
 
@@ -180,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;
@@ -221,7 +222,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayCountry(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -236,7 +237,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayLanguage(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -251,7 +252,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayScript(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -266,7 +267,7 @@
     return NULL;
   }
 
-  UnicodeString str;
+  icu::UnicodeString str;
   icuTargetLocale.locale().getDisplayVariant(icuLocale.locale(), str);
   return env->NewString(str.getBuffer(), str.length());
 }
@@ -288,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) {
@@ -336,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()));
@@ -362,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;
     }
@@ -370,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;
-    std::unique_ptr<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));
 }
 
 
@@ -495,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));
@@ -505,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).
-  std::unique_ptr<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;
@@ -584,7 +585,7 @@
     }
 
     status = U_ZERO_ERROR;
-    std::unique_ptr<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;
     }
@@ -594,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;
@@ -680,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());
 }
@@ -695,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());
 }
@@ -726,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);
 }
 
@@ -739,7 +749,7 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  std::unique_ptr<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;
   }
@@ -748,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;
   }
@@ -763,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[] = {
@@ -798,6 +808,7 @@
     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"),
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_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp
index 717bfc3..355cc78 100644
--- a/luni/src/main/native/libcore_icu_NativeConverter.cpp
+++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp
@@ -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;
     }
@@ -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 0527213..8c4a411 100644
--- a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
@@ -39,15 +39,15 @@
 #include "unicode/ustring.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);
 }
 
@@ -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_RelativeDateTimeFormatter.cpp b/luni/src/main/native/libcore_icu_RelativeDateTimeFormatter.cpp
index bba2b0e..6215d5f 100644
--- a/luni/src/main/native/libcore_icu_RelativeDateTimeFormatter.cpp
+++ b/luni/src/main/native/libcore_icu_RelativeDateTimeFormatter.cpp
@@ -31,7 +31,7 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  RelativeDateTimeFormatter* formatter = new RelativeDateTimeFormatter(
+  icu::RelativeDateTimeFormatter* formatter = new icu::RelativeDateTimeFormatter(
       icuLocale.locale(), nullptr, static_cast<UDateRelativeDateTimeFormatterStyle>(style),
       static_cast<UDisplayContext>(capitalizationContext), status);
   if (maybeThrowIcuException(env, "RelativeDateTimeFormatter::RelativeDateTimeFormatter", status)) {
@@ -43,13 +43,13 @@
 
 static void RelativeDateTimeFormatter_destroyRelativeDateTimeFormatter(JNIEnv*, jclass,
     jlong formatterAddress) {
-  delete reinterpret_cast<RelativeDateTimeFormatter*>(static_cast<uintptr_t>(formatterAddress));
+  delete reinterpret_cast<icu::RelativeDateTimeFormatter*>(static_cast<uintptr_t>(formatterAddress));
 }
 
 static jstring RelativeDateTimeFormatter_formatWithRelativeUnit(JNIEnv* env, jclass,
     jlong formatterAddress, jint quantity, jint direction, jint unit) {
-  RelativeDateTimeFormatter* formatter(reinterpret_cast<RelativeDateTimeFormatter*>(formatterAddress));
-  UnicodeString s;
+  icu::RelativeDateTimeFormatter* formatter(reinterpret_cast<icu::RelativeDateTimeFormatter*>(formatterAddress));
+  icu::UnicodeString s;
   UErrorCode status = U_ZERO_ERROR;
   // RelativeDateTimeFormatter::format() takes a double-type quantity.
   formatter->format(static_cast<double>(quantity), static_cast<UDateDirection>(direction),
@@ -63,8 +63,8 @@
 
 static jstring RelativeDateTimeFormatter_formatWithAbsoluteUnit(JNIEnv* env, jclass,
     jlong formatterAddress, jint direction, jint unit) {
-  RelativeDateTimeFormatter* formatter(reinterpret_cast<RelativeDateTimeFormatter*>(formatterAddress));
-  UnicodeString s;
+  icu::RelativeDateTimeFormatter* formatter(reinterpret_cast<icu::RelativeDateTimeFormatter*>(formatterAddress));
+  icu::UnicodeString s;
   UErrorCode status = U_ZERO_ERROR;
   formatter->format(static_cast<UDateDirection>(direction), static_cast<UDateAbsoluteUnit>(unit), s, status);
   if (maybeThrowIcuException(env, "RelativeDateTimeFormatter::format", status)) {
@@ -76,7 +76,7 @@
 
 static jstring RelativeDateTimeFormatter_combineDateAndTime(JNIEnv* env, jclass,
     jlong formatterAddress, jstring relativeDateString0, jstring timeString0) {
-  RelativeDateTimeFormatter* formatter(reinterpret_cast<RelativeDateTimeFormatter*>(formatterAddress));
+  icu::RelativeDateTimeFormatter* formatter(reinterpret_cast<icu::RelativeDateTimeFormatter*>(formatterAddress));
   ScopedJavaUnicodeString relativeDateString(env, relativeDateString0);
   if (!relativeDateString.valid()) {
     return 0;
@@ -86,7 +86,7 @@
   if (!timeString.valid()) {
     return 0;
   }
-  UnicodeString s;
+  icu::UnicodeString s;
   UErrorCode status = U_ZERO_ERROR;
   formatter->combineDateAndTime(relativeDateString.unicodeString(), timeString.unicodeString(), s, status);
   if (maybeThrowIcuException(env, "RelativeDateTimeFormatter::combineDateAndTime", status)) {
diff --git a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
index 2fa06c7..d30e7a3 100644
--- a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
+++ b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
@@ -30,27 +30,27 @@
 #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) {
@@ -68,14 +68,14 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  std::unique_ptr<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) {
@@ -88,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())) {
@@ -124,7 +124,7 @@
   }
 
   UErrorCode status = U_ZERO_ERROR;
-  std::unique_ptr<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;
   }
@@ -134,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 70bd9e4..5122a6c 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -27,25 +27,6 @@
 #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.
@@ -81,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));
     }
 }
 
@@ -259,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 4c7e372..f6af483 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -34,9 +34,11 @@
 #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>
@@ -77,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
@@ -148,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;
@@ -266,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) {
@@ -384,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) {
@@ -477,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);
@@ -516,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);
@@ -1279,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) {
@@ -1468,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;
 }
@@ -1587,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;"),
@@ -1666,6 +1876,7 @@
     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"),
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 ed0ee8b..48defc1 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -440,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);
 }
 
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index da866a6..886ab0d 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -40,7 +40,6 @@
     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 \
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index ae85595..279f934 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -81,9 +81,9 @@
     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("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));
@@ -114,54 +114,54 @@
 
     // 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 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("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 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("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 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 09–feb. de 12", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+    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 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
-    assertEquals("19–22 de ene. de 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("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun., 19 ene.–jue., 22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("lunes, 19 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.
@@ -406,6 +406,18 @@
     // 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("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+  }
+
+  // http://b/13234532
+  public void test13234532() throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+
+    int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR;
+
+    assertEquals("10 – 11 AM", formatDateRange(l, utc, 10*HOUR, 11*HOUR, flags));
+    assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11*HOUR, 13*HOUR, flags));
+    assertEquals("2 – 3 PM", formatDateRange(l, utc, 14*HOUR, 15*HOUR, flags));
   }
 }
diff --git a/luni/src/test/java/libcore/icu/ICUTest.java b/luni/src/test/java/libcore/icu/ICUTest.java
index 46f4ebe..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 {
@@ -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/RelativeDateTimeFormatterTest.java b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
index 07166bf..0b7f8c3 100644
--- a/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
+++ b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
@@ -24,6 +24,8 @@
 import static libcore.icu.RelativeDateTimeFormatter.FORMAT_ABBREV_ALL;
 import static libcore.icu.RelativeDateTimeFormatter.FORMAT_ABBREV_RELATIVE;
 import static libcore.icu.RelativeDateTimeFormatter.FORMAT_NUMERIC_DATE;
+import static libcore.icu.RelativeDateTimeFormatter.FORMAT_NO_YEAR;
+import static libcore.icu.RelativeDateTimeFormatter.FORMAT_SHOW_YEAR;
 import static libcore.icu.RelativeDateTimeFormatter.SECOND_IN_MILLIS;
 import static libcore.icu.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
 import static libcore.icu.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
@@ -344,65 +346,63 @@
   }
 
   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");
-    final long now = System.currentTimeMillis();
-    TimeZone tz = TimeZone.getDefault();
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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");
-    final long now = System.currentTimeMillis();
-    TimeZone tz = TimeZone.getDefault();
+    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));
+    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));
+    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));
+    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));
+    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));
+    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));
+    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.
@@ -608,4 +608,57 @@
                  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 0848e77..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.*;
@@ -210,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);
@@ -246,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);
@@ -268,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/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/nio/charset/CharsetEncoderTest.java b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
index 13a897a..e9ab8ae 100644
--- a/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
+++ b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
@@ -23,6 +23,7 @@
 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 {
@@ -215,4 +216,18 @@
         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/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 30185d1..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,9 +16,11 @@
 
 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.Extension;
 import com.android.org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
-import com.android.org.bouncycastle.asn1.x509.X509Extensions;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
 import com.android.org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
 
@@ -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,
-                SubjectKeyIdentifier.getInstance(keyPair.getPublic().getEncoded()));
-        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/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/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java
index 08d5590..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/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 4cda676..4f2e38c 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -261,14 +261,6 @@
         assertEquals("", failures.toString());
     }
 
-    public void testSantiago() 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-04:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.US));
-    }
-
     // http://b/7955614
     public void testApia() throws Exception {
         TimeZone tz = TimeZone.getTimeZone("Pacific/Apia");
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/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/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 1826140..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,536 +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.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.io.OutputStream;
 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 static 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 static File createTemporaryZipFile() throws IOException {
-        File result = File.createTempFile("ZipFileTest", ".zip");
-        result.deleteOnExit();
-        return result;
-    }
-
-    private static 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
-        }
-    }
-
-    // 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));
-        }
+    @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 29112fb..cd070ab 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") {
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 1a24667..07ecd12 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
@@ -198,6 +198,11 @@
         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(verifyWithDomainNamePattern("a.b.c.d", "a.b.c.d"));
         assertTrue(verifyWithDomainNamePattern("a.b.c.d", "*.b.c.d"));
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/xml/KxmlSerializerTest.java b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
index 6a75a9b..5f68a99 100644
--- a/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
+++ b/luni/src/test/java/libcore/xml/KxmlSerializerTest.java
@@ -22,7 +22,9 @@
 import junit.framework.TestCase;
 import org.kxml2.io.KXmlSerializer;
 import org.w3c.dom.Document;
+import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
 import org.xmlpull.v1.XmlSerializer;
 import static tests.support.Support_Xml.domOf;
 
@@ -87,12 +89,67 @@
         return serializer;
     }
 
+    public String fromCodePoint(int codePoint) {
+        if (codePoint > Character.MAX_VALUE) {
+            return new String(Character.toChars(codePoint));
+        }
+        return Character.toString((char) codePoint);
+    }
+
+    // http://b/17960630
+    public void testSpeakNoEvilMonkeys() throws Exception {
+        StringWriter stringWriter = new StringWriter();
+        XmlSerializer serializer = new KXmlSerializer();
+        serializer.setOutput(stringWriter);
+        serializer.startDocument("UTF-8", null);
+        serializer.startTag(NAMESPACE, "tag");
+        serializer.attribute(NAMESPACE, "attr", "a\ud83d\ude4ab");
+        serializer.text("c\ud83d\ude4ad");
+        serializer.cdsect("e\ud83d\ude4af");
+        serializer.endTag(NAMESPACE, "tag");
+        serializer.endDocument();
+        assertXmlEquals("<tag attr=\"a&#128586;b\">" +
+                        "c&#128586;d" +
+                        "<![CDATA[e]]>&#128586;<![CDATA[f]]>" +
+                        "</tag>", stringWriter.toString());
+
+        // Check we can parse what we just output.
+        Document doc = domOf(stringWriter.toString());
+        Node root = doc.getDocumentElement();
+        assertEquals("a\ud83d\ude4ab", root.getAttributes().getNamedItem("attr").getNodeValue());
+        Text text = (Text) root.getFirstChild();
+        assertEquals("c\ud83d\ude4ade\ud83d\ude4af", text.getNodeValue());
+    }
+
+    public void testBadSurrogates() throws Exception {
+        StringWriter stringWriter = new StringWriter();
+        XmlSerializer serializer = new KXmlSerializer();
+        serializer.setOutput(stringWriter);
+        serializer.startDocument("UTF-8", null);
+        serializer.startTag(NAMESPACE, "tag");
+        try {
+            serializer.attribute(NAMESPACE, "attr", "a\ud83d\u0040b");
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            serializer.text("c\ud83d\u0040d");
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            serializer.cdsect("e\ud83d\u0040f");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    // Cover all the BMP code points plus a few that require us to use surrogates.
+    private static int MAX_TEST_CODE_POINT = 0x10008;
+
     public void testInvalidCharactersInText() throws IOException {
         XmlSerializer serializer = newSerializer();
         serializer.startTag(NAMESPACE, "root");
-        for (int ch = 0; ch <= 0xffff; ++ch) {
-            final String s = Character.toString((char) ch);
-            if (isValidXmlCodePoint(ch)) {
+        for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) {
+            final String s = fromCodePoint(c);
+            if (isValidXmlCodePoint(c)) {
                 serializer.text("a" + s + "b");
             } else {
                 try {
@@ -108,9 +165,9 @@
     public void testInvalidCharactersInAttributeValues() throws IOException {
         XmlSerializer serializer = newSerializer();
         serializer.startTag(NAMESPACE, "root");
-        for (int ch = 0; ch <= 0xffff; ++ch) {
-            final String s = Character.toString((char) ch);
-            if (isValidXmlCodePoint(ch)) {
+        for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) {
+            final String s = fromCodePoint(c);
+            if (isValidXmlCodePoint(c)) {
                 serializer.attribute(NAMESPACE, "a", "a" + s + "b");
             } else {
                 try {
@@ -126,9 +183,9 @@
     public void testInvalidCharactersInCdataSections() throws IOException {
         XmlSerializer serializer = newSerializer();
         serializer.startTag(NAMESPACE, "root");
-        for (int ch = 0; ch <= 0xffff; ++ch) {
-            final String s = Character.toString((char) ch);
-            if (isValidXmlCodePoint(ch)) {
+        for (int c = 0; c <= MAX_TEST_CODE_POINT; ++c) {
+            final String s = fromCodePoint(c);
+            if (isValidXmlCodePoint(c)) {
                 serializer.cdsect("a" + s + "b");
             } else {
                 try {
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..9b7dc18 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
@@ -884,6 +884,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 4fb1ee1..0000000
--- a/luni/src/test/native/test_openssl_engine.cpp
+++ /dev/null
@@ -1,131 +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 <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/objects.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <memory>
-
-#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 std::unique_ptr<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/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index 5f5bafd..a55b47a 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -801,7 +801,7 @@
         addBoth(   "SSL_DHE_RSA_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");
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index 203c028..bd64360 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -47,6 +47,7 @@
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.Security;
+import java.security.UnrecoverableEntryException;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
@@ -260,6 +261,7 @@
         private X500Principal subject;
         private int keyUsage;
         private boolean ca;
+        private PrivateKeyEntry privateEntry;
         private PrivateKeyEntry signer;
         private Certificate rootCa;
         private final List<KeyPurposeId> extendedKeyUsages = new ArrayList<KeyPurposeId>();
@@ -314,6 +316,12 @@
             return this;
         }
 
+        /** a private key entry to use for the generation of the certificate */
+        public Builder privateEntry(PrivateKeyEntry privateEntry) {
+            this.privateEntry = privateEntry;
+            return this;
+        }
+
         /** a private key entry to be used for signing, otherwise self-sign */
         public Builder signer(PrivateKeyEntry signer) {
             this.signer = signer;
@@ -368,21 +376,32 @@
                     }
                 }
 
+                /*
+                 * This is not implemented for other key types because the logic
+                 * would be long to write and it's not needed currently.
+                 */
+                if (privateEntry != null
+                        && (keyAlgorithms.length != 1 || !"RSA".equals(keyAlgorithms[0]))) {
+                    throw new IllegalStateException(
+                            "Only reusing an existing key is implemented for RSA");
+                }
+
                 KeyStore keyStore = createKeyStore();
                 for (String keyAlgorithm : keyAlgorithms) {
                     String publicAlias  = aliasPrefix + "-public-"  + keyAlgorithm;
                     String privateAlias = aliasPrefix + "-private-" + keyAlgorithm;
                     if ((keyAlgorithm.equals("EC_RSA") || keyAlgorithm.equals("DH_RSA"))
                             && signer == null && rootCa == null) {
-                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias,
-                                   privateKey(keyStore, keyPassword, "RSA", "RSA"));
+                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
+                                privateKey(keyStore, keyPassword, "RSA", "RSA"));
                         continue;
                     } else if (keyAlgorithm.equals("DH_DSA") && signer == null && rootCa == null) {
-                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias,
+                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
                                 privateKey(keyStore, keyPassword, "DSA", "DSA"));
                         continue;
                     }
-                    createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, signer);
+                    createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, privateEntry,
+                            signer);
                 }
                 if (rootCa != null) {
                     keyStore.setCertificateEntry(aliasPrefix
@@ -416,6 +435,7 @@
                 String keyAlgorithm,
                 String publicAlias,
                 String privateAlias,
+                PrivateKeyEntry privateEntry,
                 PrivateKeyEntry signer) throws Exception {
             PrivateKey caKey;
             X509Certificate caCert;
@@ -430,42 +450,51 @@
                 caCertChain = (X509Certificate[])signer.getCertificateChain();
             }
 
-            PrivateKey privateKey;
+            final PrivateKey privateKey;
+            final PublicKey publicKey;
             X509Certificate x509c;
             if (publicAlias == null && privateAlias == null) {
                 // don't want anything apparently
                 privateKey = null;
+                publicKey = null;
                 x509c = null;
             } else {
-                // 1.) we make the keys
-                int keySize;
-                if (keyAlgorithm.equals("RSA")) {
-                    // 512 breaks SSL_RSA_EXPORT_* on RI and TLS_ECDHE_RSA_WITH_RC4_128_SHA for us
-                    keySize =  1024;
-                } else if (keyAlgorithm.equals("DH_RSA")) {
-                    keySize = 512;
-                    keyAlgorithm = "DH";
-                } else if (keyAlgorithm.equals("DSA")) {
-                    keySize = 512;
-                } else if (keyAlgorithm.equals("DH_DSA")) {
-                    keySize = 512;
-                    keyAlgorithm = "DH";
-                } else if (keyAlgorithm.equals("EC")) {
-                    keySize = 256;
-                } else if (keyAlgorithm.equals("EC_RSA")) {
-                    keySize = 256;
-                    keyAlgorithm = "EC";
+                if (privateEntry == null) {
+                    // 1a.) we make the keys
+                    int keySize;
+                    if (keyAlgorithm.equals("RSA")) {
+                        // 512 breaks SSL_RSA_EXPORT_* on RI and
+                        // TLS_ECDHE_RSA_WITH_RC4_128_SHA for us
+                        keySize = 1024;
+                    } else if (keyAlgorithm.equals("DH_RSA")) {
+                        keySize = 512;
+                        keyAlgorithm = "DH";
+                    } else if (keyAlgorithm.equals("DSA")) {
+                        keySize = 512;
+                    } else if (keyAlgorithm.equals("DH_DSA")) {
+                        keySize = 512;
+                        keyAlgorithm = "DH";
+                    } else if (keyAlgorithm.equals("EC")) {
+                        keySize = 256;
+                    } else if (keyAlgorithm.equals("EC_RSA")) {
+                        keySize = 256;
+                        keyAlgorithm = "EC";
+                    } else {
+                        throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+                    }
+
+                    KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm);
+                    kpg.initialize(keySize, new SecureRandom());
+
+                    KeyPair kp = kpg.generateKeyPair();
+                    privateKey = kp.getPrivate();
+                    publicKey = kp.getPublic();
                 } else {
-                    throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+                    // 1b.) we use the previous keys
+                    privateKey = privateEntry.getPrivateKey();
+                    publicKey = privateEntry.getCertificate().getPublicKey();
                 }
 
-                KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm);
-                kpg.initialize(keySize, new SecureRandom());
-
-                KeyPair kp = kpg.generateKeyPair();
-                privateKey = kp.getPrivate();
-                PublicKey publicKey  = kp.getPublic();
-
                 // 2.) use keys to make certificate
                 X500Principal issuer = ((caCert != null)
                                         ? caCert.getSubjectX500Principal()
@@ -820,6 +849,24 @@
     }
 
     /**
+     * Return an {@code X509Certificate that matches the given {@code alias}.
+     */
+    public KeyStore.Entry getEntryByAlias(String alias) {
+        return entryByAlias(keyStore, alias);
+    }
+
+    /**
+     * Finds an entry in the keystore by the given alias.
+     */
+    public static KeyStore.Entry entryByAlias(KeyStore keyStore, String alias) {
+        try {
+            return keyStore.getEntry(alias, null);
+        } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Create a client key store that only contains self-signed certificates but no private keys
      */
     public static KeyStore createClient(KeyStore caKeyStore) {
diff --git a/support/src/test/java/tests/resources/hyts_certLoop.jar b/support/src/test/java/tests/resources/hyts_certLoop.jar
new file mode 100644
index 0000000..cb4ebe1
--- /dev/null
+++ b/support/src/test/java/tests/resources/hyts_certLoop.jar
Binary files differ
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);
+            }
+        }
+    }
+}
diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
index 8fa2756..bfdeece 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
@@ -125,14 +125,18 @@
                     // otherwise generate.
                     // Note: tab, newline, and carriage return have already been
                     // handled above.
-                    boolean valid = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
-                    if (!valid) {
-                        reportInvalidCharacter(c);
-                    }
-                    if (unicode || c < 127) {
-                        writer.write(c);
+                    boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
+                    if (allowedInXml) {
+                        if (unicode || c < 127) {
+                            writer.write(c);
+                        } else {
+                            writer.write("&#" + ((int) c) + ";");
+                        }
+                    } else if (Character.isHighSurrogate(c) && i < s.length() - 1) {
+                        writeSurrogate(c, s.charAt(i + 1));
+                        ++i;
                     } else {
-                        writer.write("&#" + ((int) c) + ";");
+                        reportInvalidCharacter(c);
                     }
                     // END android-changed
             }
@@ -141,7 +145,7 @@
 
     // BEGIN android-added
     private static void reportInvalidCharacter(char ch) {
-        throw new IllegalArgumentException("Illegal character (" + Integer.toHexString((int) ch) + ")");
+        throw new IllegalArgumentException("Illegal character (U+" + Integer.toHexString((int) ch) + ")");
     }
     // END android-added
 
@@ -548,22 +552,41 @@
         // BEGIN android-changed: ]]> is not allowed within a CDATA,
         // so break and start a new one when necessary.
         data = data.replace("]]>", "]]]]><![CDATA[>");
-        char[] chars = data.toCharArray();
-        // We also aren't allowed any invalid characters.
-        for (char ch : chars) {
-            boolean valid = (ch >= 0x20 && ch <= 0xd7ff) ||
+        writer.write("<![CDATA[");
+        for (int i = 0; i < data.length(); ++i) {
+            char ch = data.charAt(i);
+            boolean allowedInCdata = (ch >= 0x20 && ch <= 0xd7ff) ||
                     (ch == '\t' || ch == '\n' || ch == '\r') ||
                     (ch >= 0xe000 && ch <= 0xfffd);
-            if (!valid) {
+            if (allowedInCdata) {
+                writer.write(ch);
+            } else if (Character.isHighSurrogate(ch) && i < data.length() - 1) {
+                // Character entities aren't valid in CDATA, so break out for this.
+                writer.write("]]>");
+                writeSurrogate(ch, data.charAt(++i));
+                writer.write("<![CDATA[");
+            } else {
                 reportInvalidCharacter(ch);
             }
         }
-        writer.write("<![CDATA[");
-        writer.write(chars, 0, chars.length);
         writer.write("]]>");
         // END android-changed
     }
 
+    // BEGIN android-added
+    private void writeSurrogate(char high, char low) throws IOException {
+        if (!Character.isLowSurrogate(low)) {
+            throw new IllegalArgumentException("Bad surrogate pair (U+" + Integer.toHexString((int) high) +
+                                               " U+" + Integer.toHexString((int) low) + ")");
+        }
+        // Java-style surrogate pairs aren't allowed in XML. We could use the > 3-byte encodings, but that
+        // seems likely to upset anything expecting modified UTF-8 rather than "real" UTF-8. It seems more
+        // conservative in a Java environment to use an entity reference instead.
+        int codePoint = Character.toCodePoint(high, low);
+        writer.write("&#" + codePoint + ";");
+    }
+    // END android-added
+
     public void comment(String comment) throws IOException {
         check(false);
         writer.write("<!--");