Merge "Tests for java.security.cert.Certificate(PublicKey, Provider)"
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index ff0ae37..7569737 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -87,6 +87,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) $(android_icu4j_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-all
@@ -101,6 +102,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-oj
@@ -117,6 +119,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(android_icu4j_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-libart
@@ -138,6 +141,7 @@
 LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) $(openjdk_lambda_duplicate_stub_files)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-lambda-stubs
@@ -156,6 +160,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_DEX_PREOPT := false
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
@@ -190,6 +195,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(android_icu4j_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_DEX_PREOPT := false
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
@@ -262,6 +268,7 @@
     LOCAL_JAVA_LIBRARIES := core-oj core-libart okhttp bouncycastle
     LOCAL_STATIC_JAVA_LIBRARIES := testng
     LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_DX_FLAGS := --core-library
     LOCAL_MODULE_TAGS := optional
     LOCAL_JAVA_LANGUAGE_VERSION := 1.8
     LOCAL_MODULE := core-ojtests
@@ -286,6 +293,7 @@
         okhttp \
         testng
     LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_DX_FLAGS := --core-library
     LOCAL_MODULE_TAGS := optional
     LOCAL_JAVA_LANGUAGE_VERSION := 1.8
     LOCAL_MODULE := core-ojtests-public
@@ -312,6 +320,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-all-hostdex
@@ -326,6 +335,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-oj-hostdex
@@ -342,6 +352,7 @@
 LOCAL_JAVA_RESOURCE_DIRS := $(android_icu4j_resource_dirs)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-libart-hostdex
@@ -356,6 +367,7 @@
 LOCAL_SRC_FILES := $(openjdk_lambda_stub_files) $(openjdk_lambda_duplicate_stub_files)
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_MODULE := core-lambda-stubs-hostdex
@@ -428,6 +440,7 @@
         okhttp-hostdex
     LOCAL_STATIC_JAVA_LIBRARIES := testng-hostdex
     LOCAL_JAVACFLAGS := $(local_javac_flags)
+    LOCAL_DX_FLAGS := --core-library
     LOCAL_MODULE_TAGS := optional
     LOCAL_JAVA_LANGUAGE_VERSION := 1.8
     LOCAL_MODULE := core-ojtests-hostdex
diff --git a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
index 29a7a89..7ce09c9 100644
--- a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
+++ b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
@@ -247,6 +247,8 @@
     int OP_USHR_INT_LIT8                = 0x00e2;
     int OP_INVOKE_POLYMORPHIC           = 0x00fa;
     int OP_INVOKE_POLYMORPHIC_RANGE     = 0x00fb;
+    int OP_INVOKE_CUSTOM                = 0x00fc;
+    int OP_INVOKE_CUSTOM_RANGE          = 0x00fd;
     // END(libcore-opcodes)
 
     /** Never implemented; do not use. */
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index 99d4d63..bab2f46 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -18,6 +18,7 @@
 
 import java.io.File;
 import java.net.URL;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
@@ -68,6 +69,22 @@
         }
     }
 
+    /**
+     * Constructs an instance.
+     *
+     * dexFile must be an in-memory representation of a full dexFile.
+     *
+     * @param dexFiles the array of in-memory dex files containing classes.
+     * @param parent the parent class loader
+     *
+     * @hide
+     */
+    public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
+        // TODO We should support giving this a library search path maybe.
+        super(parent);
+        this.pathList = new DexPathList(this, dexFiles);
+    }
+
     @Override
     protected Class<?> findClass(String name) throws ClassNotFoundException {
         List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 42cdac0..2a95450 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -21,7 +21,9 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import libcore.io.Libcore;
@@ -115,6 +117,12 @@
         //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
     }
 
+    DexFile(ByteBuffer buf) throws IOException {
+        mCookie = openInMemoryDexFile(buf);
+        mInternalCookie = mCookie;
+        mFileName = null;
+    }
+
     /**
      * Opens a DEX file from a given filename, using a specified file
      * to hold the optimized data.
@@ -230,7 +238,11 @@
     }
 
     @Override public String toString() {
-        return getName();
+        if (mFileName != null) {
+            return getName();
+        } else {
+            return "InMemoryDexFile[cookie=" + Arrays.toString((long[]) mCookie) + "]";
+        }
     }
 
     /**
@@ -374,6 +386,17 @@
                                  elements);
     }
 
+    private static Object openInMemoryDexFile(ByteBuffer buf) throws IOException {
+        if (buf.isDirect()) {
+            return createCookieWithDirectBuffer(buf, buf.position(), buf.limit());
+        } else {
+            return createCookieWithArray(buf.array(), buf.position(), buf.limit());
+        }
+    }
+
+    private static native Object createCookieWithDirectBuffer(ByteBuffer buf, int start, int end);
+    private static native Object createCookieWithArray(byte[] buf, int start, int end);
+
     /*
      * Returns true if the dex file is backed by a valid oat file.
      */
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 444d483..b056d20 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -22,14 +22,15 @@
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import libcore.io.ClassPathURLStreamHandler;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
-import libcore.io.ClassPathURLStreamHandler;
 
 import static android.system.OsConstants.S_ISDIR;
 
@@ -75,6 +76,42 @@
     private IOException[] dexElementsSuppressedExceptions;
 
     /**
+     * Construct an instance.
+     *
+     * @param definingContext the context in which any as-yet unresolved
+     * classes should be defined
+     *
+     * @param dexFiles the bytebuffers containing the dex files that we should load classes from.
+     */
+    public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles) {
+        if (definingContext == null) {
+            throw new NullPointerException("definingContext == null");
+        }
+        if (dexFiles == null) {
+            throw new NullPointerException("dexFiles == null");
+        }
+        if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
+            throw new NullPointerException("dexFiles contains a null Buffer!");
+        }
+
+        this.definingContext = definingContext;
+        // TODO It might be useful to let in-memory dex-paths have native libraries.
+        this.nativeLibraryDirectories = Collections.emptyList();
+        this.systemNativeLibraryDirectories =
+                splitPaths(System.getProperty("java.library.path"), true);
+        this.nativeLibraryPathElements = makePathElements(this.systemNativeLibraryDirectories);
+
+        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
+        this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);
+        if (suppressedExceptions.size() > 0) {
+            this.dexElementsSuppressedExceptions =
+                    suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
+        } else {
+            dexElementsSuppressedExceptions = null;
+        }
+    }
+
+    /**
      * Constructs an instance.
      *
      * @param definingContext the context in which any as-yet unresolved
@@ -245,6 +282,25 @@
         return result;
     }
 
+    private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
+            List<IOException> suppressedExceptions) {
+        Element[] elements = new Element[dexFiles.length];
+        int elementPos = 0;
+        for (ByteBuffer buf : dexFiles) {
+            try {
+                DexFile dex = new DexFile(buf);
+                elements[elementPos++] = new Element(dex);
+            } catch (IOException suppressed) {
+                System.logE("Unable to load dex file: " + buf, suppressed);
+                suppressedExceptions.add(suppressed);
+            }
+        }
+        if (elementPos != elements.length) {
+            elements = Arrays.copyOf(elements, elementPos);
+        }
+        return elements;
+    }
+
     /**
      * Makes an array of dex/resource path elements, one per element of
      * the given array.
@@ -490,8 +546,8 @@
         for (Element e : dexElements) {
             String dexPath = e.getDexPath();
             if (dexPath != null) {
-                // Add the element to the list only if it is a file.
-                // A null dex path signals the element is a resource directory.
+                // Add the element to the list only if it is a file. A null dex path signals the
+                // element is a resource directory or an in-memory dex file.
                 dexPaths.add(dexPath);
             }
         }
@@ -523,6 +579,11 @@
             this.path = dexZipPath;
         }
 
+        public Element(DexFile dexFile) {
+            this.dexFile = dexFile;
+            this.path = null;
+        }
+
         public Element(File path) {
           this.path = path;
           this.dexFile = null;
diff --git a/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java b/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java
index b3c9f8f..0fa1e45 100644
--- a/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java
@@ -17,15 +17,24 @@
 package dalvik.system;
 
 import java.nio.ByteBuffer;
-import sun.misc.Cleaner;
 
 /**
  * A {@link ClassLoader} implementation that loads classes from a
  * buffer containing a DEX file. This can be used to execute code that
  * has not been written to the local file system.
  */
-public final class InMemoryDexClassLoader extends ClassLoader {
-    private final DexData dexData;
+public final class InMemoryDexClassLoader extends BaseDexClassLoader {
+    /**
+     * Create an in-memory DEX class loader with the given dex buffers.
+     *
+     * @param dexBuffers array of buffers containing DEX files between
+     *                       <tt>buffer.position()</tt> and <tt>buffer.limit()</tt>.
+     * @param parent the parent class loader for delegation.
+     * @hide
+     */
+    public InMemoryDexClassLoader(ByteBuffer[] dexBuffers, ClassLoader parent) {
+        super(dexBuffers, parent);
+    }
 
     /**
      * Creates a new in-memory DEX class loader.
@@ -35,85 +44,6 @@
      * @param parent the parent class loader for delegation.
      */
     public InMemoryDexClassLoader(ByteBuffer dexBuffer, ClassLoader parent) {
-        super(parent);
-        if (dexBuffer == null) {
-            throw new NullPointerException("dexData == null");
-        }
-        this.dexData = new DexData(dexBuffer);
-    }
-
-    @Override
-    protected Class<?> findClass(String name) throws ClassNotFoundException {
-        try {
-            return dexData.findClass(name, this);
-        } catch (Exception e) {
-            throw new ClassNotFoundException("Didn't find class \"" + name + "\"", e);
-        }
-    }
-
-    @Override
-    protected synchronized Package getPackage(String name) {
-        // This is duplicated from BaseDexClassLoader.getPackage which
-        // has an extensive comment on why this needs to be defined.
-        if (name == null || name.isEmpty()) {
-            return null;
-        }
-
-        Package pkg = super.getPackage(name);
-        if (pkg == null) {
-            pkg = definePackage(name, "Unknown", "0.0", "Unknown",
-                                "Unknown", "0.0", "Unknown", null);
-        }
-        return pkg;
-    }
-
-    /**
-     * Representation of native resources associated with DEX files loaded by
-     * InMemoryDexClassLoader.
-     */
-    private static final class DexData {
-        private final long cookie;
-
-        DexData(ByteBuffer buffer) {
-            if (buffer.isDirect()) {
-                cookie = initializeWithDirectBuffer(buffer, buffer.position(), buffer.limit());
-            } else {
-                cookie = initializeWithArray(buffer.array(), buffer.position(), buffer.limit());
-            }
-
-            if (cookie != 0) {
-                // Register for clean-up when this instance is phantom
-                // reachable. Cleaner instances are inserted in a global
-                // queue to preserve liveness. DexDataDeallocator must not
-                // have a pointer to DexData otherwise it will prevent
-                // cleaning and the freeing of the native resources.
-                Cleaner.create(this, new DexDataDeallocator(cookie));
-            }
-        }
-
-        protected Class<?> findClass(String name, ClassLoader loader) {
-            return findClass(name, loader, cookie);
-        }
-
-        private static native long initializeWithDirectBuffer(ByteBuffer buffer,
-                                                              int start, int end);
-        private static native long initializeWithArray(byte[] array, int start, int end);
-        private static native void uninitialize(long cookie);
-        private native Class findClass(String name, ClassLoader loader, long cookie);
-    }
-
-    /**
-     * Helper class to release native resources associated with DexData instances.
-     */
-    private static final class DexDataDeallocator implements Runnable {
-        private final long cookie;
-
-        DexDataDeallocator(long cookie) {
-            this.cookie = cookie;
-        }
-
-        public void run() {
-            DexData.uninitialize(cookie);
-        }
+        this(new ByteBuffer[] { dexBuffer }, parent);
     }
 }
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index f96ed0b..85b52f8 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -16,6 +16,7 @@
 
 package dalvik.system;
 
+import dalvik.annotation.optimization.FastNative;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.HashMap;
@@ -106,6 +107,7 @@
      *
      * @return the time in milliseconds, or -1 if the debugger is not connected
      */
+    @FastNative
     public static native long lastDebuggerActivity();
 
     /**
@@ -114,6 +116,7 @@
      *
      * @return true if debugging is enabled
      */
+    @FastNative
     public static native boolean isDebuggingEnabled();
 
     /**
@@ -121,6 +124,7 @@
      *
      * @return true if (and only if) a debugger is connected
      */
+    @FastNative
     public static native boolean isDebuggerConnected();
 
     /**
@@ -251,6 +255,7 @@
      * @return the CPU usage. A value of -1 means the system does not support
      *         this feature.
      */
+    @FastNative
     public static native long threadCpuTimeNanos();
 
     /**
@@ -291,6 +296,7 @@
     /**
      * Dumps a list of loaded class to the log file.
      */
+    @FastNative
     public static native void printLoadedClasses(int flags);
 
     /**
@@ -298,6 +304,7 @@
      *
      * @return the number of loaded classes
      */
+    @FastNative
     public static native int getLoadedClassCount();
 
     /**
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
index 7717fd9..5a2c06d 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
@@ -16,6 +16,7 @@
 
 package org.apache.harmony.dalvik.ddmc;
 
+import dalvik.annotation.optimization.FastNative;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -97,6 +98,7 @@
     }
 
     /* send a chunk to the DDM server */
+    @FastNative
     native private static void nativeSendChunk(int type, byte[] data,
         int offset, int length);
 
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
index 01293b0..786efe7 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
@@ -16,6 +16,8 @@
 
 package org.apache.harmony.dalvik.ddmc;
 
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * Declarations for some VM-internal DDM stuff.
  */
@@ -40,6 +42,7 @@
      * @return true on success.  false if 'when' is bad or if there was
      *         an internal error.
      */
+    @FastNative
     native public static boolean heapInfoNotify(int when);
 
     /**
@@ -74,11 +77,13 @@
      * Return a boolean indicating whether or not the "recent allocation"
      * feature is currently enabled.
      */
+    @FastNative
     native public static boolean getRecentAllocationStatus();
 
     /**
      * Fill a buffer with data on recent heap allocations.
      */
+    @FastNative
     native public static byte[] getRecentAllocations();
 }
 
diff --git a/dex/src/main/java/com/android/dex/DexFormat.java b/dex/src/main/java/com/android/dex/DexFormat.java
index c598eee..97771d8 100644
--- a/dex/src/main/java/com/android/dex/DexFormat.java
+++ b/dex/src/main/java/com/android/dex/DexFormat.java
@@ -23,14 +23,38 @@
 public final class DexFormat {
     private DexFormat() {}
 
+    /** API level to target in order to generate invoke-polymorphic */
+    public static final int API_INVOKE_POLYMORPHIC = 26;
+
+    /** API level to target in order to pass through default and static interface methods */
+    public static final int API_DEFAULT_INTERFACE_METHODS = 24;
+
+    /** API level to target in order to suppress extended opcode usage */
+    public static final int API_NO_EXTENDED_OPCODES = 13;
+
     /**
      * API level to target in order to produce the most modern file
      * format
      */
-    public static final int API_CURRENT = 24;
+    public static final int API_CURRENT = API_INVOKE_POLYMORPHIC;
 
-    /** API level to target in order to suppress extended opcode usage */
-    public static final int API_NO_EXTENDED_OPCODES = 13;
+    /** dex file version number for API level 26 and earlier */
+    public static final String VERSION_FOR_API_26 = "038";
+
+    /** dex file version number for API level 24 and earlier */
+    public static final String VERSION_FOR_API_24 = "037";
+
+    /** dex file version number for API level 13 and earlier */
+    public static final String VERSION_FOR_API_13 = "035";
+
+    /**
+     * Dex file version number for dalvik.
+     * <p>
+     * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
+     * completed and is not considered a valid dex file format.
+     * </p>
+     */
+    public static final String VERSION_CURRENT = VERSION_FOR_API_26;
 
     /**
      * file name of the primary {@code .dex} file inside an
@@ -45,18 +69,6 @@
     public static final String MAGIC_SUFFIX = "\0";
 
     /**
-     * Dex file version number for dalvik.
-     * <p>
-     * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
-     * completed and is not considered a valid dex file format.
-     * </p>
-     */
-    public static final String VERSION_CURRENT = "037";
-
-    /** dex file version number for API level 13 and earlier */
-    public static final String VERSION_FOR_API_13 = "035";
-
-    /**
      * value used to indicate endianness of file contents
      */
     public static final int ENDIAN_TAG = 0x12345678;
@@ -91,10 +103,14 @@
 
         String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]);
 
-        if (version.equals(VERSION_CURRENT)) {
-            return API_CURRENT;
-        } else if (version.equals(VERSION_FOR_API_13)) {
+        if (version.equals(VERSION_FOR_API_13)) {
             return API_NO_EXTENDED_OPCODES;
+        } else if (version.equals(VERSION_FOR_API_24)) {
+            return API_DEFAULT_INTERFACE_METHODS;
+        } else if (version.equals(VERSION_FOR_API_26)) {
+            return API_INVOKE_POLYMORPHIC;
+        } else if (version.equals(VERSION_CURRENT)) {
+            return API_CURRENT;
         }
 
         return -1;
@@ -108,6 +124,10 @@
 
         if (targetApiLevel >= API_CURRENT) {
             version = VERSION_CURRENT;
+        } else if (targetApiLevel >= API_INVOKE_POLYMORPHIC) {
+            version = VERSION_FOR_API_26;
+        } else if (targetApiLevel >= API_DEFAULT_INTERFACE_METHODS) {
+            version = VERSION_FOR_API_24;
         } else {
             version = VERSION_FOR_API_13;
         }
@@ -117,6 +137,6 @@
 
     public static boolean isSupportedDexMagic(byte[] magic) {
         int api = magicToApi(magic);
-        return api == API_NO_EXTENDED_OPCODES || api == API_CURRENT;
+        return api > 0;
     }
 }
diff --git a/dex/src/main/java/com/android/dex/TableOfContents.java b/dex/src/main/java/com/android/dex/TableOfContents.java
index 583f195..b33a749 100644
--- a/dex/src/main/java/com/android/dex/TableOfContents.java
+++ b/dex/src/main/java/com/android/dex/TableOfContents.java
@@ -36,6 +36,8 @@
     public final Section fieldIds = new Section(0x0004);
     public final Section methodIds = new Section(0x0005);
     public final Section classDefs = new Section(0x0006);
+    public final Section callSiteIds = new Section(0x0007);
+    public final Section methodHandles = new Section(0x0008);
     public final Section mapList = new Section(0x1000);
     public final Section typeLists = new Section(0x1001);
     public final Section annotationSetRefLists = new Section(0x1002);
@@ -48,9 +50,9 @@
     public final Section encodedArrays = new Section(0x2005);
     public final Section annotationsDirectories = new Section(0x2006);
     public final Section[] sections = {
-            header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList,
-            typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas,
-            debugInfos, annotations, encodedArrays, annotationsDirectories
+        header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds,
+        methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes,
+        stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories
     };
 
     public int apiLevel;
@@ -76,7 +78,12 @@
         byte[] magic = headerIn.readByteArray(8);
 
         if (!DexFormat.isSupportedDexMagic(magic)) {
-            throw new DexException("Unexpected magic: " + Arrays.toString(magic));
+            String msg =
+                    String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, "
+                                  + "0x%02x, 0x%02x, 0x%02x, 0x%02x]",
+                                  magic[0], magic[1], magic[2], magic[3],
+                                  magic[4], magic[5], magic[6], magic[7]);
+            throw new DexException(msg);
         }
 
         apiLevel = DexFormat.magicToApi(magic);
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index 5dc7ad8..cd24094 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -45,38 +45,20 @@
   description: "Some tests depend on ICU data, which has changed. Others make assumptions about floating point rounding",
   result: EXEC_FAILED,
   names: [
-    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_BigDecimalExceptionOrder",
     "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_DateTimeConversion",
-    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionE",
-    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionF",
-    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatConversionG",
-    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_FloatDoubleBigDecimalExceptionOrder",
     "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_GeneralConversionOther",
     "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_LineSeparator",
-    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Percent",
-    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Width"
+    "org.apache.harmony.tests.java.util.FormatterTest#test_formatLjava_lang_String$Ljava_lang_Object_Percent"
   ]
 },
 {
   description: "(Needs investigation) Some tests make assertions that don't make sense, others use broken port allocation logic.",
   result: EXEC_FAILED,
   names: [
-    "org.apache.harmony.tests.java.net.Inet6AddressTest#test_getByNameLjava_lang_String",
-    "org.apache.harmony.tests.java.net.InetAddressTest#test_getByNameLjava_lang_String",
     "org.apache.harmony.tests.java.net.InetAddressTest#test_isReachableLjava_net_NetworkInterfaceII_loopbackInterface"
   ]
 },
 {
-  description: "(Needs investigation) Test failures from the harmony import of external/apache-harmony/archive",
-  bug: 12189307,
-  result: EXEC_FAILED,
-  names: [
-    "org.apache.harmony.tests.java.util.jar.ManifestTest#testNul",
-    "org.apache.harmony.tests.java.util.jar.ManifestTest#testRead",
-    "org.apache.harmony.tests.java.util.jar.ManifestTest#testStreamConstructor"
-  ]
-},
-{
   description: "Potentially flakey because they rely on a specific local TCP port being free.",
   result: EXEC_FAILED,
   names: [
@@ -123,13 +105,6 @@
   ]
 },
 {
-  description: "Suffers from side effect of other, currently unknown test",
-  result: EXEC_FAILED,
-  names: [
-      "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnectionTest#testProxyAuthorization"
-  ]
-},
-{
   description: "Support_TestWebServer requires isolation.",
   result: EXEC_FAILED,
   names: [
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index a5764f5..da96678 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1294,19 +1294,6 @@
   name: "org.apache.harmony.tests.java.lang.MathTest#test_powDD"
 },
 {
-  description: "Known failures in PropertiesTest: We don't deal with comments in store()",
-  bug: 11686302,
-  result: EXEC_FAILED,
-  names: [
-    "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario0",
-    "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario1",
-    "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario2",
-    "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario3",
-    "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario9",
-    "org.apache.harmony.tests.java.util.PropertiesTest#testStore_scenario11"
-  ]
-},
-{
   description: "Known failures in URLTest and URLDecoderTest",
   bug: 11686814,
   names: [
@@ -1413,14 +1400,6 @@
   ]
 },
 {
-  description: "Known precision issue in DecimalFormat",
-  bug: 17656132,
-  names: [
-    "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDouble_bug17656132",
-    "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDouble_roundingProblemCases"
-  ]
-},
-{
   description: "Known failure in GregorianCalendarTest",
   bug: 12778197,
   name: "org.apache.harmony.tests.java.util.GregorianCalendarTest#test_computeTime"
@@ -1435,7 +1414,7 @@
 },
 {
   description: "libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale assumes fallback to locale other than en_US_POSIX.",
-  bug: 17374604,
+  bug: 17422813,
   names: [
     "libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale"
   ]
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java
index 5a80fde..34dd0fa 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/PhantomReferenceTest.java
@@ -49,7 +49,24 @@
     /**
      * java.lang.Runtime#gc()
      */
-    public void test_gcInteraction() {
+    public void test_gcInteraction_Runtime() {
+        check_gcInteraction(() -> { Runtime.getRuntime().gc(); } );
+    }
+
+    /**
+     * Checks that the sequence {@link System#gc()}, {@link System#runFinalization()}}
+     * also has the effect as asserted for {@link Runtime#gc()} elsewhere. The
+     * conditions under which System.gc() results in a garbage collection are an
+     * implementation detail not guaranteed by documentation.
+     */
+    public void test_gcInteraction_System() {
+        check_gcInteraction(() -> {
+            System.gc();
+            System.runFinalization();
+        } );
+    }
+
+    private void check_gcInteraction(Runnable gc) {
         class TestPhantomReference<T> extends PhantomReference<T> {
             public TestPhantomReference(T referent,
                     ReferenceQueue<? super T> q) {
@@ -58,7 +75,7 @@
             public boolean enqueue() {
                 // Initiate another GC from inside enqueue() to
                 // see if it causes any problems inside the VM.
-                Runtime.getRuntime().gc();
+                gc.run();
                 return super.enqueue();
             }
         }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java
index 785a303..b32a965 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/Inet6AddressTest.java
@@ -681,49 +681,6 @@
                 .isIPv4CompatibleAddress());
     }
 
-    public void test_getByNameLjava_lang_String() throws Exception {
-        // ones to add "::255.255.255.255", "::FFFF:0.0.0.0",
-        // "0.0.0.0.0.0::255.255.255.255", "F:F:F:F:F:F:F:F",
-        // "[F:F:F:F:F:F:F:F]"
-        String validIPAddresses[] = { "::1.2.3.4", "::", "::", "1::0", "1::",
-                "::1", "0", /* jdk1.5 accepts 0 as valid */
-                "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
-                "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255",
-                "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0.0.0.0" };
-
-        String invalidIPAddresses[] = { "FFFF:FFFF" };
-
-        for (int i = 0; i < validIPAddresses.length; i++) {
-
-            InetAddress.getByName(validIPAddresses[i]);
-
-            //exercise positive cache
-            InetAddress.getByName(validIPAddresses[i]);
-
-            if (!validIPAddresses[i].equals("0")) {
-                String tempIPAddress = "[" + validIPAddresses[i] + "]";
-                InetAddress.getByName(tempIPAddress);
-            }
-        }
-
-        for (int i = 0; i < invalidIPAddresses.length; i++) {
-            try {
-                InetAddress.getByName(invalidIPAddresses[i]);
-                fail("Invalid IP address incorrectly recognized as valid: "
-                        + invalidIPAddresses[i]);
-            } catch (Exception e) {
-            }
-
-            //exercise negative cache
-            try {
-                InetAddress.getByName(invalidIPAddresses[i]);
-                fail("Invalid IP address incorrectly recognized as valid: "
-                        + invalidIPAddresses[i]);
-            } catch (Exception e) {
-            }
-        }
-    }
-
     public void test_getByAddressLString$BI() throws UnknownHostException {
         try {
             Inet6Address.getByAddress("123", null, 0);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
index 4e41c2a..7e232cc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
@@ -112,23 +112,6 @@
     }
 
     /**
-     * java.net.InetAddress#getByName(java.lang.String)
-     */
-    public void test_getByNameLjava_lang_String() throws Exception {
-        // Test for method java.net.InetAddress
-        // java.net.InetAddress.getByName(java.lang.String)
-        InetAddress ia2 = InetAddress.getByName("127.0.0.1");
-
-        // TODO : Test to ensure all the address formats are recognized
-        InetAddress i = InetAddress.getByName("1.2.3");
-        assertEquals("1.2.0.3", i.getHostAddress());
-        i = InetAddress.getByName("1.2");
-        assertEquals("1.0.0.2", i.getHostAddress());
-        i = InetAddress.getByName(String.valueOf(0xffffffffL));
-        assertEquals("255.255.255.255", i.getHostAddress());
-    }
-
-    /**
      * java.net.InetAddress#getHostAddress()
      */
     public void test_getHostAddress() throws Exception {
@@ -383,12 +366,11 @@
 
         NetworkInterface loopbackInterface = null;
         ArrayList<InetAddress> localAddresses = new ArrayList<InetAddress>();
-        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface
-                .getNetworkInterfaces();
+        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+        assertNotNull(networkInterfaces);
         while (networkInterfaces.hasMoreElements()) {
             NetworkInterface networkInterface = networkInterfaces.nextElement();
-            Enumeration<InetAddress> addresses = networkInterface
-                    .getInetAddresses();
+            Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
             while (addresses.hasMoreElements()) {
                 InetAddress address = addresses.nextElement();
                 if (address.isLoopbackAddress()) {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
index 902caef..29470d1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
@@ -1699,7 +1699,7 @@
         // double 9999999999.999998 is decimal 9999999999.9999980926513671875
         assertEquals("9999999999.999998", df.format(9999999999.999998));
         // double 1E23 is decimal 99999999999999991611392
-        assertEquals("9999999999999999", df.format(1E23));
+        assertEquals("99999999999999990000000", df.format(1E23));
     }
 
     public void test_getDecimalFormatSymbols() {
diff --git a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
index fc1632c..ca2add3 100644
--- a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
+++ b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
@@ -1221,21 +1221,61 @@
                 fail("Unexpected thread termination");
             else if (millisElapsedSince(startTime) > timeoutMillis) {
                 threadAssertTrue(thread.isAlive());
-                return;
+                fail("timed out waiting for thread to enter wait state");
             }
             Thread.yield();
         }
     }
 
     /**
-     * Waits up to LONG_DELAY_MS for the given thread to enter a wait
-     * state: BLOCKED, WAITING, or TIMED_WAITING.
+     * Spin-waits up to the specified number of milliseconds for the given
+     * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+     * and additionally satisfy the given condition.
+     */
+    void waitForThreadToEnterWaitState(
+        Thread thread, long timeoutMillis, Callable<Boolean> waitingForGodot) {
+        long startTime = 0L;
+        for (;;) {
+            Thread.State s = thread.getState();
+            if (s == Thread.State.BLOCKED ||
+                s == Thread.State.WAITING ||
+                s == Thread.State.TIMED_WAITING) {
+                try {
+                    if (waitingForGodot.call())
+                        return;
+                } catch (Throwable fail) { threadUnexpectedException(fail); }
+            }
+            else if (s == Thread.State.TERMINATED)
+                fail("Unexpected thread termination");
+            else if (startTime == 0L)
+                startTime = System.nanoTime();
+            else if (millisElapsedSince(startTime) > timeoutMillis) {
+                threadAssertTrue(thread.isAlive());
+                fail("timed out waiting for thread to enter wait state");
+            }
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+     * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
      */
     void waitForThreadToEnterWaitState(Thread thread) {
         waitForThreadToEnterWaitState(thread, LONG_DELAY_MS);
     }
 
     /**
+     * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+     * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+     * and additionally satisfy the given condition.
+     */
+    void waitForThreadToEnterWaitState(
+        Thread thread, Callable<Boolean> waitingForGodot) {
+        waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, waitingForGodot);
+    }
+
+    /**
      * Returns the number of milliseconds since time given by
      * startNanoTime, which must have been previously returned from a
      * call to {@link System#nanoTime()}.
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
index 05fc689..efe5a58 100644
--- a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
@@ -17,6 +17,7 @@
 import java.util.NoSuchElementException;
 import java.util.Queue;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
@@ -750,9 +751,11 @@
             }});
 
         threadStarted.await();
-        waitForThreadToEnterWaitState(t);
-        assertEquals(1, q.getWaitingConsumerCount());
-        assertTrue(q.hasWaitingConsumer());
+        Callable<Boolean> oneConsumer
+            = new Callable<Boolean>() { public Boolean call() {
+                return q.hasWaitingConsumer()
+                && q.getWaitingConsumerCount() == 1; }};
+        waitForThreadToEnterWaitState(t, oneConsumer);
 
         assertTrue(q.offer(one));
         assertEquals(0, q.getWaitingConsumerCount());
@@ -789,8 +792,11 @@
             }});
 
         threadStarted.await();
-        waitForThreadToEnterWaitState(t);
-        assertEquals(1, q.size());
+        Callable<Boolean> oneElement
+            = new Callable<Boolean>() { public Boolean call() {
+                return !q.isEmpty() && q.size() == 1; }};
+        waitForThreadToEnterWaitState(t, oneElement);
+
         assertSame(five, q.poll());
         checkEmpty(q);
         awaitTermination(t);
diff --git a/jsr166-tests/src/test/java/jsr166/PhaserTest.java b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
index 673e556..1219017 100644
--- a/jsr166-tests/src/test/java/jsr166/PhaserTest.java
+++ b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
@@ -527,7 +527,7 @@
             }});
 
         await(pleaseArrive);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        waitForThreadToEnterWaitState(t);
         assertEquals(0, phaser.arrive());
         awaitTermination(t);
 
@@ -555,7 +555,7 @@
             }});
 
         await(pleaseArrive);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        waitForThreadToEnterWaitState(t);
         t.interrupt();
         assertEquals(0, phaser.arrive());
         awaitTermination(t);
@@ -571,20 +571,20 @@
     public void testArriveAndAwaitAdvanceAfterInterrupt() {
         final Phaser phaser = new Phaser();
         assertEquals(0, phaser.register());
-        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
 
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() {
                 Thread.currentThread().interrupt();
                 assertEquals(0, phaser.register());
-                pleaseInterrupt.countDown();
+                pleaseArrive.countDown();
                 assertTrue(Thread.currentThread().isInterrupted());
                 assertEquals(1, phaser.arriveAndAwaitAdvance());
-                assertTrue(Thread.currentThread().isInterrupted());
+                assertTrue(Thread.interrupted());
             }});
 
-        await(pleaseInterrupt);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t);
         Thread.currentThread().interrupt();
         assertEquals(1, phaser.arriveAndAwaitAdvance());
         assertTrue(Thread.interrupted());
@@ -605,11 +605,11 @@
                 assertFalse(Thread.currentThread().isInterrupted());
                 pleaseInterrupt.countDown();
                 assertEquals(1, phaser.arriveAndAwaitAdvance());
-                assertTrue(Thread.currentThread().isInterrupted());
+                assertTrue(Thread.interrupted());
             }});
 
         await(pleaseInterrupt);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        waitForThreadToEnterWaitState(t);
         t.interrupt();
         Thread.currentThread().interrupt();
         assertEquals(1, phaser.arriveAndAwaitAdvance());
@@ -784,7 +784,7 @@
         assertEquals(THREADS, phaser.getArrivedParties());
         assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
         for (Thread thread : threads)
-            waitForThreadToEnterWaitState(thread, SHORT_DELAY_MS);
+            waitForThreadToEnterWaitState(thread);
         for (Thread thread : threads)
             assertTrue(thread.isAlive());
         assertState(phaser, 0, THREADS + 1, 1);
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index e53e103..44930a6 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -16,6 +16,7 @@
 
 package dalvik.system;
 
+import dalvik.annotation.optimization.FastNative;
 import java.lang.ref.FinalizerReference;
 import java.util.HashMap;
 import java.util.Map;
@@ -102,11 +103,13 @@
     /**
      * Returns whether the VM is running in 64-bit mode.
      */
+    @FastNative
     public native boolean is64Bit();
 
     /**
      * Returns whether the VM is running with JNI checking enabled.
      */
+    @FastNative
     public native boolean isCheckJniEnabled();
 
     /**
@@ -255,6 +258,7 @@
      * This is used to implement native allocations on the Java heap, such as DirectByteBuffers
      * and Bitmaps.
      */
+    @FastNative
     public native Object newNonMovableArray(Class<?> componentType, int length);
 
     /**
@@ -262,12 +266,14 @@
      * avoiding any padding after the array. The amount of padding varies depending on the
      * componentType and the memory allocator implementation.
      */
+    @FastNative
     public native Object newUnpaddedArray(Class<?> componentType, int minLength);
 
     /**
      * Returns the address of array[0]. This differs from using JNI in that JNI might lie and
      * give you the address of a copy of the array when in forcecopy mode.
      */
+    @FastNative
     public native long addressOf(Object array);
 
     /**
@@ -285,11 +291,13 @@
     /**
      * Returns true if either a Java debugger or native debugger is active.
      */
+    @FastNative
     public native boolean isDebuggerActive();
 
     /**
      * Returns true if native debugging is on.
      */
+    @FastNative
     public native boolean isNativeDebuggable();
 
     /**
diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java
index b69ab60..ef911c4 100644
--- a/libart/src/main/java/dalvik/system/VMStack.java
+++ b/libart/src/main/java/dalvik/system/VMStack.java
@@ -16,6 +16,8 @@
 
 package dalvik.system;
 
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * Provides a limited interface to the Dalvik VM stack. This class is mostly
  * used for implementing security checks.
@@ -29,6 +31,7 @@
      * @return the requested class loader, or {@code null} if this is the
      *         bootstrap class loader.
      */
+    @FastNative
     native public static ClassLoader getCallingClassLoader();
 
     /**
@@ -45,12 +48,14 @@
      *
      * @return the requested class, or {@code null}.
      */
+    @FastNative
     native public static Class<?> getStackClass2();
 
     /**
      * Returns the first ClassLoader on the call stack that isn't the
      * bootstrap class loader.
      */
+    @FastNative
     public native static ClassLoader getClosestUserClassLoader();
 
     /**
@@ -61,6 +66,7 @@
      * @return an array of stack trace elements, or null if the thread
      *      doesn't have a stack trace (e.g. because it exited)
      */
+    @FastNative
     native public static StackTraceElement[] getThreadStackTrace(Thread t);
 
     /**
@@ -74,6 +80,7 @@
      *      desired. Unused elements will be filled with null values.
      * @return the number of elements filled
      */
+    @FastNative
     native public static int fillStackTraceElements(Thread t,
         StackTraceElement[] stackTraceElements);
 }
diff --git a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
index 13e9317..5a84c8e 100644
--- a/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
+++ b/libart/src/main/java/java/lang/AndroidHardcodedSystemProperties.java
@@ -106,6 +106,9 @@
 
         // Hardcode default value for AVA. b/28174137
         { "com.sun.security.preserveOldDCEncoding", null },
+
+        // Hardcode default value for LogManager. b/28174137
+        { "java.util.logging.manager", null },
     };
 }
 
diff --git a/libart/src/main/java/java/lang/CaseMapper.java b/libart/src/main/java/java/lang/CaseMapper.java
index 7f9d2e3..66d5030 100644
--- a/libart/src/main/java/java/lang/CaseMapper.java
+++ b/libart/src/main/java/java/lang/CaseMapper.java
@@ -50,7 +50,7 @@
             return ICU.toLowerCase(s, locale);
         }
 
-        String newString = null;
+        char[] newValue = null;
         for (int i = 0, end = s.length(); i < end; ++i) {
             char ch = s.charAt(i);
             char newCh;
@@ -63,13 +63,14 @@
                 newCh = Character.toLowerCase(ch);
             }
             if (ch != newCh) {
-                if (newString == null) {
-                    newString = StringFactory.newStringFromString(s);
+                if (newValue == null) {
+                    newValue = new char[end];
+                    s.getCharsNoCheck(0, end, newValue, 0);
                 }
-                newString.setCharAt(i, newCh);
+                newValue[i] = newCh;
             }
         }
-        return newString != null ? newString : s;
+        return newValue != null ? new String(newValue) : s;
     }
 
     /**
@@ -152,9 +153,8 @@
         }
 
         char[] output = null;
-        String newString = null;
         int i = 0;
-        for (int o = 0, end = count; o < end; o++) {
+        for (int o = 0; o < count; o++) {
             char ch = s.charAt(o);
             if (Character.isHighSurrogate(ch)) {
                 return ICU.toUpperCase(s, locale);
@@ -170,10 +170,10 @@
                 if (output != null) {
                     output[i++] = upch;
                 } else if (ch != upch) {
-                    if (newString == null) {
-                        newString = StringFactory.newStringFromString(s);
-                    }
-                    newString.setCharAt(o, upch);
+                    output = new char[count];
+                    i = o;
+                    s.getCharsNoCheck(0, i, output, 0);
+                    output[i++] = upch;
                 }
             } else {
                 int target = index * 3;
@@ -181,11 +181,7 @@
                 if (output == null) {
                     output = new char[count + (count / 6) + 2];
                     i = o;
-                    if (newString != null) {
-                        System.arraycopy(newString.toCharArray(), 0, output, 0, i);
-                    } else {
-                        System.arraycopy(s.toCharArray(), 0, output, 0, i);
-                    }
+                    s.getCharsNoCheck(0, i, output, 0);
                 } else if (i + (val3 == 0 ? 1 : 2) >= output.length) {
                     char[] newoutput = new char[output.length + (count / 6) + 3];
                     System.arraycopy(output, 0, newoutput, 0, output.length);
@@ -202,11 +198,7 @@
             }
         }
         if (output == null) {
-            if (newString != null) {
-                return newString;
-            } else {
-                return s;
-            }
+            return s;
         }
         return output.length == i || output.length - i < 8 ? new String(0, i, output) : new String(output, 0, i);
     }
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
index 8465a24..7e5bdff 100644
--- a/libart/src/main/java/java/lang/DexCache.java
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -32,6 +32,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import com.android.dex.Dex;
 
 /**
@@ -48,12 +49,24 @@
     private long dexFile;
 
     /**
+     * References to CallSite (C array pointer) as they become resolved following
+     * interpreter semantics.
+     */
+    private long resolvedCallSites;
+
+    /**
      * References to fields (C array pointer) as they become resolved following
      * interpreter semantics. May refer to fields defined in other dex files.
      */
     private long resolvedFields;
 
     /**
+     * References to MethodType (C array pointer) as they become resolved following
+     * interpreter semantics.
+     */
+    private long resolvedMethodTypes;
+
+    /**
      * References to methods (C array pointer) as they become resolved following
      * interpreter semantics. May refer to methods defined in other dex files.
      */
@@ -72,10 +85,9 @@
     private long strings;
 
     /**
-     * References to MethodType (C array pointer) as they become resolved following
-     * interpreter semantics.
+     * The number of elements in the native call sites array.
      */
-    private long resolvedMethodTypes;
+    private int numResolvedCallSites;
 
     /**
      * The number of elements in the native resolvedFields array.
@@ -83,6 +95,11 @@
     private int numResolvedFields;
 
     /**
+     * The number of elements in the native method types array.
+     */
+    private int numResolvedMethodTypes;
+
+    /**
      * The number of elements in the native resolvedMethods array.
      */
     private int numResolvedMethods;
@@ -97,11 +114,6 @@
      */
     private int numStrings;
 
-    /**
-     * The number of elements in the native method types array.
-     */
-    private int numResolvedMethodTypes;
-
     // Only created by the VM.
     private DexCache() {}
 
@@ -118,10 +130,15 @@
         return result;
     }
 
+    @FastNative
     native Class<?> getResolvedType(int typeIndex);
+    @FastNative
     native String getResolvedString(int stringIndex);
+    @FastNative
     native void setResolvedType(int typeIndex, Class<?> type);
+    @FastNative
     native void setResolvedString(int stringIndex, String string);
+    @FastNative
     private native Dex getDexNative();
 }
 
diff --git a/libart/src/main/java/java/lang/StringFactory.java b/libart/src/main/java/java/lang/StringFactory.java
index 0a89740..208a657 100644
--- a/libart/src/main/java/java/lang/StringFactory.java
+++ b/libart/src/main/java/java/lang/StringFactory.java
@@ -17,6 +17,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
@@ -53,6 +54,7 @@
         return newStringFromBytes(data, offset, byteCount, Charset.defaultCharset());
     }
 
+    @FastNative
     public static native String newStringFromBytes(byte[] data, int high, int offset, int byteCount);
 
     public static String newStringFromBytes(byte[] data, int offset, int byteCount, String charsetName) throws UnsupportedEncodingException {
@@ -219,8 +221,10 @@
     }
 
     // The char array passed as {@code java_data} must not be a null reference.
+    @FastNative
     static native String newStringFromChars(int offset, int charCount, char[] data);
 
+    @FastNative
     public static native String newStringFromString(String toCopy);
 
     public static String newStringFromStringBuffer(StringBuffer stringBuffer) {
diff --git a/libart/src/main/java/java/lang/VMClassLoader.java b/libart/src/main/java/java/lang/VMClassLoader.java
index f5e65c8..d44f888 100644
--- a/libart/src/main/java/java/lang/VMClassLoader.java
+++ b/libart/src/main/java/java/lang/VMClassLoader.java
@@ -16,6 +16,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
@@ -85,6 +86,7 @@
         return list;
     }
 
+    @FastNative
     native static Class findLoadedClass(ClassLoader cl, String name);
 
     /**
diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java
index 61bfb6c..d7e803e 100644
--- a/luni/src/main/java/java/lang/ref/FinalizerReference.java
+++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java
@@ -16,6 +16,8 @@
 
 package java.lang.ref;
 
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * @hide
  */
@@ -129,7 +131,9 @@
         throw new AssertionError("newly-created live Sentinel not on list!");
     }
 
+    @FastNative
     private final native T getReferent();
+    @FastNative
     private native boolean makeCircularListIfUnenqueued();
 
     /**
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 18aae9b..111c584 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -201,7 +201,7 @@
 
     @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
         BlockGuard.getThreadPolicy().onReadFromDisk();
-        if ((mode & O_ACCMODE) != O_RDONLY) {
+        if ((flags & O_ACCMODE) != O_RDONLY) {
             BlockGuard.getThreadPolicy().onWriteToDisk();
         }
         return os.open(path, flags, mode);
diff --git a/luni/src/main/java/libcore/io/Memory.java b/luni/src/main/java/libcore/io/Memory.java
index e148457..ba7398d 100644
--- a/luni/src/main/java/libcore/io/Memory.java
+++ b/luni/src/main/java/libcore/io/Memory.java
@@ -17,6 +17,7 @@
 
 package libcore.io;
 
+import dalvik.annotation.optimization.FastNative;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -150,6 +151,7 @@
      */
     public static native void memmove(Object dstObject, int dstOffset, Object srcObject, int srcOffset, long byteCount);
 
+    @FastNative
     public static native byte peekByte(long address);
 
     public static int peekInt(long address, boolean swap) {
@@ -159,6 +161,7 @@
         }
         return result;
     }
+    @FastNative
     private static native int peekIntNative(long address);
 
     public static long peekLong(long address, boolean swap) {
@@ -168,6 +171,7 @@
         }
         return result;
     }
+    @FastNative
     private static native long peekLongNative(long address);
 
     public static short peekShort(long address, boolean swap) {
@@ -177,6 +181,7 @@
         }
         return result;
     }
+    @FastNative
     private static native short peekShortNative(long address);
 
     public static native void peekByteArray(long address, byte[] dst, int dstOffset, int byteCount);
@@ -187,6 +192,7 @@
     public static native void peekLongArray(long address, long[] dst, int dstOffset, int longCount, boolean swap);
     public static native void peekShortArray(long address, short[] dst, int dstOffset, int shortCount, boolean swap);
 
+    @FastNative
     public static native void pokeByte(long address, byte value);
 
     public static void pokeInt(long address, int value, boolean swap) {
@@ -195,6 +201,7 @@
         }
         pokeIntNative(address, value);
     }
+    @FastNative
     private static native void pokeIntNative(long address, int value);
 
     public static void pokeLong(long address, long value, boolean swap) {
@@ -203,6 +210,7 @@
         }
         pokeLongNative(address, value);
     }
+    @FastNative
     private static native void pokeLongNative(long address, long value);
 
     public static void pokeShort(long address, short value, boolean swap) {
@@ -211,6 +219,7 @@
         }
         pokeShortNative(address, value);
     }
+    @FastNative
     private static native void pokeShortNative(long address, short value);
 
     public static native void pokeByteArray(long address, byte[] src, int offset, int count);
diff --git a/luni/src/main/java/libcore/util/CharsetUtils.java b/luni/src/main/java/libcore/util/CharsetUtils.java
index 5163dba..bab6f53 100644
--- a/luni/src/main/java/libcore/util/CharsetUtils.java
+++ b/luni/src/main/java/libcore/util/CharsetUtils.java
@@ -16,6 +16,8 @@
 
 package libcore.util;
 
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * Various special-case charset conversions (for performance).
  *
@@ -26,18 +28,21 @@
      * Returns a new byte array containing the bytes corresponding to the characters in the given
      * string, encoded in US-ASCII. Unrepresentable characters are replaced by (byte) '?'.
      */
+    @FastNative
     public static native byte[] toAsciiBytes(String s, int offset, int length);
 
     /**
      * Returns a new byte array containing the bytes corresponding to the characters in the given
      * string, encoded in ISO-8859-1. Unrepresentable characters are replaced by (byte) '?'.
      */
+    @FastNative
     public static native byte[] toIsoLatin1Bytes(String s, int offset, int length);
 
     /**
      * Returns a new byte array containing the bytes corresponding to the characters in the given
      * string, encoded in UTF-8. All characters are representable in UTF-8.
      */
+    @FastNative
     public static native byte[] toUtf8Bytes(String s, int offset, int length);
 
     /**
@@ -64,6 +69,7 @@
      *     value[i] = (ch <= 0x7f) ? ch : REPLACEMENT_CHAR;
      * }
      */
+    @FastNative
     public static native void asciiBytesToChars(byte[] bytes, int offset, int length, char[] chars);
 
     /**
@@ -73,6 +79,7 @@
      *     value[i] = (char) (data[start++] & 0xff);
      * }
      */
+    @FastNative
     public static native void isoLatin1BytesToChars(byte[] bytes, int offset, int length, char[] chars);
 
     private CharsetUtils() {
diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp
index c29a6c7..5c42be6 100644
--- a/luni/src/main/native/java_math_NativeBN.cpp
+++ b/luni/src/main/native/java_math_NativeBN.cpp
@@ -216,43 +216,33 @@
   if (bytes.get() == NULL) {
     return;
   }
+
+  if (bytesLen == 0) {
+    BN_zero(ret);
+    return;
+  }
+
   const unsigned char* bytesTmp = reinterpret_cast<const unsigned char*>(bytes.get());
 
-  if ((bytes[0] & 0x80) == 0) {
-    // Positive value: we can use the existing BN implementation for unsigned big endian bytes.
+  if (!BN_bin2bn(bytesTmp, bytesLen, ret)) {
+    throwException(env);
+    return;
+  }
 
-    if (!BN_bin2bn(bytesTmp, bytesLen, ret)) {
-      throwException(env);
-      return;
-    }
-    BN_set_negative(ret, false);
-  } else {
-    // Negative value: we need to interpret the twos complement representation
-    //
-    // This uses the fact that in the 2c rep,
-    //    - x = ~ x + 1
-    //
-    // TODO (varomodt): add BN bitwise ops to make this easier.
+  // Use the high bit to determine the sign in twos-complement.
+  BN_set_negative(ret, (bytes[0] & 0x80) != 0);
 
-    jbyteArray oppositeBytes = env->NewByteArray(bytesLen);
-    ScopedByteArrayRW opp(env, oppositeBytes);
-    uint8_t* oppTmp = reinterpret_cast<uint8_t*>(opp.get());
-
-    for (int i = 0; i < bytesLen; i++) {
-      oppTmp[i] = ~ bytesTmp[i];
-    }
-
-    if (!BN_bin2bn(oppTmp, bytesLen, ret)) {
+  if (BN_is_negative(ret)) {
+    // For negative values, BN_bin2bn doesn't interpret the twos-complement
+    // representation, so ret is now (- value - 2^N). We can use nnmod_pow2 to set
+    // ret to (-value).
+    if (!BN_nnmod_pow2(ret, ret, bytesLen * 8)) {
       throwException(env);
       return;
     }
 
-    BN_set_negative(ret, true);
-
-    if (!BN_sub(ret, ret, BN_value_one())) {
-      throwException(env);
-      return;
-    }
+    // And now we correct the sign.
+    BN_set_negative(ret, 1);
   }
 }
 
@@ -372,39 +362,31 @@
   BN_set_negative(toBigNum(b), n);
 }
 
-// TODO (varomodt): add BN_is_pow2 to make this fast.
-static bool isPowerOfTwo(JNIEnv* env, BIGNUM* x) {
-  int bits = BN_num_bits(x);
-
-  if (bits == 0) {
-    return false;
-  }
-
-  BIGNUM tmp;
-  BN_init(&tmp);
-
-  if (!BN_copy(&tmp, x)) {
-    BN_free(&tmp);
-    throwException(env);
-    return false;
-  }
-
-  // If our value is 2^bits, then masking off one bit will make it zero.
-  BN_mask_bits(&tmp, bits - 1);
-  bool ret = BN_is_zero(&tmp);
-  BN_free(&tmp);
-  return ret;
-}
-
 static int NativeBN_bitLength(JNIEnv* env, jclass, jlong a0) {
   if (!oneValidHandle(env, a0)) return JNI_FALSE;
   BIGNUM* a = toBigNum(a0);
 
-  if (BN_is_negative(a)) {
-    return isPowerOfTwo(env, a) ? BN_num_bits(a) - 1 : BN_num_bits(a);
-  } else {
+  // If a is not negative, we can use BN_num_bits directly.
+  if (!BN_is_negative(a)) {
     return BN_num_bits(a);
   }
+
+  // In the negative case, the number of bits in a is the same as the number of bits in |a|,
+  // except one less when |a| is a power of two.
+  BIGNUM positiveA;
+  BN_init(&positiveA);
+
+  if (!BN_copy(&positiveA, a)) {
+    BN_free(&positiveA);
+    throwException(env);
+    return -1;
+  }
+
+  BN_set_negative(&positiveA, false);
+  int numBits = BN_is_pow2(&positiveA) ? BN_num_bits(&positiveA) - 1 : BN_num_bits(&positiveA);
+
+  BN_free(&positiveA);
+  return numBits;
 }
 
 static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass, jlong a, int n) {
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index 05680db..1acb8f4 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -18,6 +18,7 @@
 
 #include "JNIHelp.h"
 #include "JniConstants.h"
+#include "nativehelper/jni_macros.h"
 #include "Portability.h"
 #include "ScopedBytes.h"
 #include "ScopedPrimitiveArray.h"
@@ -289,27 +290,27 @@
 
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(Memory, memmove, "(Ljava/lang/Object;ILjava/lang/Object;IJ)V"),
-    NATIVE_METHOD(Memory, peekByte, "!(J)B"),
+    FAST_NATIVE_METHOD(Memory, peekByte, "(J)B"),
     NATIVE_METHOD(Memory, peekByteArray, "(J[BII)V"),
     NATIVE_METHOD(Memory, peekCharArray, "(J[CIIZ)V"),
     NATIVE_METHOD(Memory, peekDoubleArray, "(J[DIIZ)V"),
     NATIVE_METHOD(Memory, peekFloatArray, "(J[FIIZ)V"),
-    NATIVE_METHOD(Memory, peekIntNative, "!(J)I"),
+    FAST_NATIVE_METHOD(Memory, peekIntNative, "(J)I"),
     NATIVE_METHOD(Memory, peekIntArray, "(J[IIIZ)V"),
-    NATIVE_METHOD(Memory, peekLongNative, "!(J)J"),
+    FAST_NATIVE_METHOD(Memory, peekLongNative, "(J)J"),
     NATIVE_METHOD(Memory, peekLongArray, "(J[JIIZ)V"),
-    NATIVE_METHOD(Memory, peekShortNative, "!(J)S"),
+    FAST_NATIVE_METHOD(Memory, peekShortNative, "(J)S"),
     NATIVE_METHOD(Memory, peekShortArray, "(J[SIIZ)V"),
-    NATIVE_METHOD(Memory, pokeByte, "!(JB)V"),
+    FAST_NATIVE_METHOD(Memory, pokeByte, "(JB)V"),
     NATIVE_METHOD(Memory, pokeByteArray, "(J[BII)V"),
     NATIVE_METHOD(Memory, pokeCharArray, "(J[CIIZ)V"),
     NATIVE_METHOD(Memory, pokeDoubleArray, "(J[DIIZ)V"),
     NATIVE_METHOD(Memory, pokeFloatArray, "(J[FIIZ)V"),
-    NATIVE_METHOD(Memory, pokeIntNative, "!(JI)V"),
+    FAST_NATIVE_METHOD(Memory, pokeIntNative, "(JI)V"),
     NATIVE_METHOD(Memory, pokeIntArray, "(J[IIIZ)V"),
-    NATIVE_METHOD(Memory, pokeLongNative, "!(JJ)V"),
+    FAST_NATIVE_METHOD(Memory, pokeLongNative, "(JJ)V"),
     NATIVE_METHOD(Memory, pokeLongArray, "(J[JIIZ)V"),
-    NATIVE_METHOD(Memory, pokeShortNative, "!(JS)V"),
+    FAST_NATIVE_METHOD(Memory, pokeShortNative, "(JS)V"),
     NATIVE_METHOD(Memory, pokeShortArray, "(J[SIIZ)V"),
     NATIVE_METHOD(Memory, unsafeBulkGet, "(Ljava/lang/Object;II[BIIZ)V"),
     NATIVE_METHOD(Memory, unsafeBulkPut, "([BIILjava/lang/Object;IIZ)V"),
diff --git a/luni/src/test/java/libcore/java/lang/OldSystemTest.java b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
index dc5741f..224e697 100644
--- a/luni/src/test/java/libcore/java/lang/OldSystemTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
@@ -255,22 +255,9 @@
         }
     }
 
-    public void test_gc() {
-        Runtime rt =  Runtime.getRuntime();
-        Vector<StringBuffer> vec = new Vector<StringBuffer>();
-        long beforeTest = rt.totalMemory() - rt.freeMemory();
-        while (rt.totalMemory() - rt.freeMemory() < beforeTest * 2) {
-             vec.add(new StringBuffer(1000));
-        }
-        long beforeGC = rt.totalMemory() - rt.freeMemory();
-        vec = null;
-        System.gc();
-        System.runFinalization();
-        long afterGC = rt.totalMemory() - rt.freeMemory();
-        assertTrue("memory was not released after calling System.gc()." +
-                "before gc: " + beforeGC + "; after gc: " + afterGC,
-                beforeGC > afterGC);
-    }
+    // Android-changed: test_gc() was deleted. PhantomReferenceTest provides basic
+    // coverage for the fact that System.gc() executes a garbage collection if
+    // followed by System.runFinalization().
 
     public void test_getenv() {
         // String[] props = { "PATH", "HOME", "USER"};
diff --git a/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java
new file mode 100644
index 0000000..5ba87f9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.lang.invoke;
+
+import junit.framework.TestCase;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+import java.lang.invoke.VolatileCallSite;
+import java.lang.invoke.WrongMethodTypeException;
+
+import static java.lang.invoke.MethodHandles.Lookup.*;
+
+public class CallSitesTest extends TestCase {
+    public void test_ConstantCallSite() throws Throwable {
+        final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+        final MethodHandle mh =
+                MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+        final ConstantCallSite site = new ConstantCallSite(mh);
+        assertEquals(mh, site.getTarget());
+        assertEquals(type, site.type());
+
+        int n = (int) site.dynamicInvoker().invokeExact(7, 37);
+        assertEquals(44, n);
+        try {
+            site.setTarget(mh);
+            fail();
+        } catch (UnsupportedOperationException e) {
+        }
+    }
+
+    public void test_EarlyBoundMutableCallSite() throws Throwable {
+        final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+        final MethodHandle add2 =
+                MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+        MutableCallSite site = new MutableCallSite(type);
+        commonMutableCallSitesTest(site, add2);
+    }
+
+    public void test_EarlyBoundVolatileCallSite() throws Throwable {
+        final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+        final MethodHandle add2 =
+                MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+        VolatileCallSite site = new VolatileCallSite(type);
+        commonMutableCallSitesTest(site, add2);
+    }
+
+    public void test_LateBoundMutableCallSite() throws Throwable {
+        final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+        MutableCallSite site = new MutableCallSite(type);
+        assertEquals(type, site.type());
+        try {
+            int dummy = (int) site.getTarget().invokeExact(1, 1);
+            fail();
+        } catch (IllegalStateException e) {
+            assertEquals("uninitialized call site", e.getMessage());
+        }
+        final MethodHandle add2 =
+                MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+        site.setTarget(add2);
+        commonMutableCallSitesTest(site, add2);
+    }
+
+    public void test_LateBoundVolatileCallSite() throws Throwable {
+        final MethodType type = MethodType.methodType(int.class, int.class, int.class);
+        VolatileCallSite site = new VolatileCallSite(type);
+        assertEquals(type, site.type());
+        try {
+            int dummy = (int) site.getTarget().invokeExact(1, 1);
+            fail();
+        } catch (IllegalStateException e) {
+            assertEquals("uninitialized call site", e.getMessage());
+        }
+        final MethodHandle add2 =
+                MethodHandles.lookup().findStatic(CallSitesTest.class, "add2", type);
+        site.setTarget(add2);
+        commonMutableCallSitesTest(site, add2);
+    }
+
+    private static void commonMutableCallSitesTest(CallSite site,
+                                                  MethodHandle firstTarget) throws Throwable{
+        site.setTarget(firstTarget);
+        site.setTarget(firstTarget);
+
+        int x = (int) firstTarget.invokeExact(2, 6);
+        assertEquals(8, x);
+
+        int y = (int) site.getTarget().invokeExact(2, 6);
+        assertEquals(8, y);
+
+        int z = (int) site.dynamicInvoker().invokeExact(2, 6);
+        assertEquals(8, z);
+
+        try {
+            site.setTarget(null);
+            fail();
+        } catch (NullPointerException e) {
+        }
+
+        final MethodHandle other = MethodHandles.lookup().findStatic(
+            CallSitesTest.class, "add3",
+            MethodType.methodType(int.class, int.class, int.class, int.class));
+        try {
+            site.setTarget(other);
+            fail();
+        } catch (WrongMethodTypeException e) {
+        }
+        assertEquals(firstTarget, site.getTarget());
+
+        final MethodHandle sub2 =
+                MethodHandles.lookup().findStatic(CallSitesTest.class, "sub2", firstTarget.type());
+        site.setTarget(sub2);
+        assertEquals(sub2, site.getTarget());
+        assertEquals(100, (int) site.dynamicInvoker().invokeExact(147, 47));
+    }
+
+    private static int add2(int x, int y) {
+        return x + y;
+    }
+
+    private static int add3(int x, int y, int z) {
+        return x + y + z;
+    }
+
+    private static int sub2(int x, int y) {
+        return x - y;
+    }
+}
+
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
index 4c65161..9bf02ef 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
@@ -16,16 +16,20 @@
 
 package libcore.java.lang.invoke;
 
+import java.lang.Thread;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.WrongMethodTypeException;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 import junit.framework.TestCase;
 
 public class MethodHandleCombinersTest extends TestCase {
 
+    static final int TEST_THREAD_ITERATIONS = 1000;
+
     public static void testThrowException() throws Throwable {
         MethodHandle handle = MethodHandles.throwException(String.class,
                 IllegalArgumentException.class);
@@ -117,6 +121,23 @@
         }
     }
 
+    public static void testDropArguments_List() throws Throwable {
+        MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+                "dropArguments_delegate",
+                MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
+
+        MethodHandle transform = MethodHandles.dropArguments(
+                delegate, 0, Arrays.asList(int.class, Object.class));
+
+        transform.invokeExact(45, new Object(), "foo", 42l);
+        transform.invoke(45, new Object(), "foo", 42l);
+
+        // Check that asType works as expected.
+        transform = transform.asType(MethodType.methodType(void.class,
+                new Class<?>[]{short.class, Object.class, String.class, long.class}));
+        transform.invokeExact((short) 45, new Object(), "foo", 42l);
+    }
+
     public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
             throws Throwable {
         if (exceptionMessage != null) {
@@ -1193,7 +1214,13 @@
 
         Object ret = handle.invokeWithArguments(new Object[]{"a", "b", "c"});
         assertEquals(42, (int) ret);
-        handle.invokeWithArguments(new String[]{"a", "b", "c"});
+        ret = handle.invokeWithArguments(new String[]{"a", "b", "c"});
+        assertEquals(42, (int) ret);
+
+        // Also test the versions that take a List<?> instead of an array.
+        ret = handle.invokeWithArguments(Arrays.asList(new Object[] {"a", "b", "c"}));
+        assertEquals(42, (int) ret);
+        ret = handle.invokeWithArguments(Arrays.asList(new String[]{"a", "b", "c"}));
         assertEquals(42, (int) ret);
 
         // Pass in an array that's too small. Should throw an IAE.
@@ -1204,6 +1231,14 @@
         } catch (WrongMethodTypeException expected) {
         }
 
+        try {
+            handle.invokeWithArguments(Arrays.asList(new Object[]{"a", "b"}));
+            fail();
+        } catch (IllegalArgumentException expected) {
+        } catch (WrongMethodTypeException expected) {
+        }
+
+
         // Test implicit unboxing.
         MethodType methodType2 = MethodType.methodType(int.class,
                 new Class<?>[]{String.class, int.class});
@@ -1633,4 +1668,260 @@
         } catch (IllegalArgumentException expected) {
         }
     }
+
+    // An exception thrown on worker threads and re-thrown on the main thread.
+    static Throwable workerException = null;
+
+    private static void invokeMultiThreaded(final MethodHandle mh) throws Throwable {
+        // Create enough worker threads to be oversubscribed in bid to force some parallelism.
+        final int threadCount = Runtime.getRuntime().availableProcessors() + 1;
+        final Thread threads [] = new Thread [threadCount];
+
+        // Launch worker threads and iterate invoking method handle.
+        for (int i = 0; i < threadCount; ++i) {
+            threads[i] = new Thread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            for (int j = 0; j < TEST_THREAD_ITERATIONS; ++j) {
+                                mh.invoke();
+                            }
+                        } catch (Throwable t) {
+                            workerException = t;
+                            fail("Unexpected exception " + workerException);
+                        }
+                    }});
+            threads[i].start();
+        }
+
+        // Wait for completion
+        for (int i = 0; i < threadCount; ++i) {
+            threads[i].join();
+        }
+
+        // Fail on main thread to avoid test appearing to complete successfully.
+        Throwable t = workerException;
+        workerException = null;
+        if (t != null) {
+            throw t;
+        }
+    }
+
+    public static void testDropInsertArgumentsMultithreaded() throws Throwable {
+        MethodHandle delegate = MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+                "dropArguments_delegate",
+                MethodType.methodType(void.class, new Class<?>[]{String.class, long.class}));
+        MethodHandle mh = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
+        mh = MethodHandles.insertArguments(mh, 0, 3333, "bogon", "foo", 42);
+        invokeMultiThreaded(mh);
+    }
+
+    private static void exceptionHandler_delegate(NumberFormatException e, int x, int y, long z)
+            throws Throwable {
+        assertEquals(e.getClass(), NumberFormatException.class);
+        assertEquals(e.getMessage(), "fake");
+        assertEquals(x, 66);
+        assertEquals(y, 51);
+        assertEquals(z, 20000000000l);
+    }
+
+    public static void testThrowCatchExceptionMultiThreaded() throws Throwable {
+        MethodHandle thrower = MethodHandles.throwException(void.class,
+                                                            NumberFormatException.class);
+        thrower = MethodHandles.dropArguments(thrower, 0, int.class, int.class, long.class);
+        MethodHandle handler = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "exceptionHandler_delegate",
+            MethodType.methodType(void.class, NumberFormatException.class,
+                                  int.class, int.class, long.class));
+        MethodHandle catcher =
+            MethodHandles.catchException(thrower, NumberFormatException.class, handler);
+        MethodHandle caller = MethodHandles.insertArguments(catcher, 0, 66, 51, 20000000000l,
+                                                            new NumberFormatException("fake"));
+        invokeMultiThreaded(caller);
+    }
+
+    private static void testTargetAndFallback_delegate(MethodHandle mh) throws Throwable {
+        String actual = (String) mh.invoke("target", 42, 56);
+        assertEquals("target", actual);
+        actual = (String) mh.invoke("blah", 41, 56);
+        assertEquals("fallback", actual);
+    }
+
+    public static void testGuardWithTestMultiThreaded() throws Throwable {
+        MethodHandle test =
+                MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+                                                  "testGuardWithTest_test",
+                                                  MethodType.methodType(boolean.class,
+                                                                        new Class<?>[]{String.class,
+                                                                                    long.class}));
+        final MethodType type = MethodType.methodType(String.class,
+                new Class<?>[]{String.class, long.class, int.class});
+        final MethodHandle target =
+                MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+                                                  "testGuardWithTest_target", type);
+        final MethodHandle fallback =
+                MethodHandles.lookup().findStatic(MethodHandleCombinersTest.class,
+                                                  "testGuardWithTest_fallback", type);
+        MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
+        MethodHandle tester = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class,
+            "testTargetAndFallback_delegate",
+            MethodType.methodType(void.class, MethodHandle.class));
+        invokeMultiThreaded(MethodHandles.insertArguments(tester, 0, adapter));
+    }
+
+    private static void arrayElementSetterGetter_delegate(MethodHandle getter,
+                                                          MethodHandle setter,
+                                                          int [] values)
+            throws Throwable{
+        for (int i = 0; i < values.length; ++i) {
+            int value = i * 13;
+            setter.invoke(values, i, value);
+            assertEquals(values[i], value);
+            assertEquals(getter.invoke(values, i), values[i]);
+        }
+    }
+
+    public static void testReferenceArrayGetterMultiThreaded() throws Throwable {
+        MethodHandle getter = MethodHandles.arrayElementGetter(int[].class);
+        MethodHandle setter = MethodHandles.arrayElementSetter(int[].class);
+        MethodHandle mh = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class,
+            "arrayElementSetterGetter_delegate",
+            MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, int[].class));
+        mh = MethodHandles.insertArguments(mh, 0, getter, setter,
+                                           new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 });
+        invokeMultiThreaded(mh);
+    }
+
+    private static void checkConstant_delegate(MethodHandle mh, double value) throws Throwable {
+        assertEquals(mh.invoke(), value);
+    }
+
+    public static void testConstantMultithreaded() throws Throwable {
+        final double value = 7.77e77;
+        MethodHandle constant = MethodHandles.constant(double.class, value);
+        MethodHandle mh = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkConstant_delegate",
+            MethodType.methodType(void.class, MethodHandle.class, double.class));
+        mh = MethodHandles.insertArguments(mh, 0, constant, value);
+        invokeMultiThreaded(mh);
+    }
+
+    private static void checkIdentity_delegate(MethodHandle mh, char value) throws Throwable {
+        assertEquals(mh.invoke(value), value);
+    }
+
+    public static void testIdentityMultiThreaded() throws Throwable {
+        final char value = 'z';
+        MethodHandle identity = MethodHandles.identity(char.class);
+        MethodHandle mh = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkIdentity_delegate",
+            MethodType.methodType(void.class, MethodHandle.class, char.class));
+        mh = MethodHandles.insertArguments(mh, 0, identity, value);
+        invokeMultiThreaded(mh);
+    }
+
+    private static int multiplyByTwo(int x) { return x * 2; }
+    private static int divideByTwo(int x) { return x / 2; }
+    private static void assertMethodHandleInvokeEquals(MethodHandle mh, int value) throws Throwable{
+        assertEquals(mh.invoke(value), value);
+    }
+
+    public static void testFilterReturnValueMultiThreaded() throws Throwable {
+        MethodHandle target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "multiplyByTwo",
+            MethodType.methodType(int.class, int.class));
+        MethodHandle filter = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "divideByTwo",
+            MethodType.methodType(int.class, int.class));
+        MethodHandle filtered = MethodHandles.filterReturnValue(target, filter);
+        assertEquals(filtered.invoke(33), 33);
+        MethodHandle mh = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "assertMethodHandleInvokeEquals",
+            MethodType.methodType(void.class, MethodHandle.class, int.class));
+        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, filtered, 77));
+    }
+
+    public static void compareStringAndFloat(String s, float f) {
+        assertEquals(s, Float.toString(f));
+    }
+
+    public static void testPermuteArgumentsMultiThreaded() throws Throwable {
+        MethodHandle mh = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "compareStringAndFloat",
+            MethodType.methodType(void.class, String.class, float.class));
+        mh = MethodHandles.permuteArguments(
+            mh, MethodType.methodType(void.class, float.class, String.class), 1, 0);
+        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, 2.22f, "2.22"));
+    }
+
+    public static void testSpreadInvokerMultiThreaded() throws Throwable {
+        MethodType methodType = MethodType.methodType(
+            int.class, new Class<?>[]{String.class, String.class, String.class});
+        MethodHandle delegate = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "spreadReferences", methodType);
+        MethodHandle mh = delegate.asSpreader(String[].class, 3);
+        mh = MethodHandles.insertArguments(mh, 0, new Object[] { new String [] { "a", "b", "c" }});
+        invokeMultiThreaded(mh);
+    }
+
+    public static void testCollectorMultiThreaded() throws Throwable {
+        MethodHandle trailingRef = MethodHandles.lookup().findStatic(
+                MethodHandleCombinersTest.class, "collectCharSequence",
+                MethodType.methodType(int.class, String.class, CharSequence[].class));
+        MethodHandle mh = trailingRef.asCollector(String[].class, 2);
+        mh = MethodHandles.insertArguments(mh, 0, "a", "b", "c");
+        invokeMultiThreaded(mh);
+    }
+
+    public static void testFilterArgumentsMultiThreaded() throws Throwable {
+        MethodHandle filter1 = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "filter1",
+            MethodType.methodType(String.class, char.class));
+        MethodHandle filter2 = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "filter2",
+            MethodType.methodType(char.class, String.class));
+        MethodHandle target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "filterTarget",
+            MethodType.methodType(int.class, String.class, char.class, String.class, char.class));
+        MethodHandle adapter = MethodHandles.filterArguments(target, 2, filter1, filter2);
+        invokeMultiThreaded(MethodHandles.insertArguments(adapter, 0, "a", 'b', 'c', "dXXXXX"));
+    }
+
+    private static void checkStringResult_delegate(MethodHandle mh,
+                                                   String expected) throws Throwable {
+        assertEquals(mh.invoke(), expected);
+    }
+
+    public static void testCollectArgumentsMultiThreaded() throws Throwable {
+        MethodHandle filter = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "filter",
+            MethodType.methodType(String.class, char.class, char.class));
+        MethodHandle target = MethodHandles.lookup().findStatic(
+                MethodHandleCombinersTest.class, "target",
+                MethodType.methodType(String.class, String.class, String.class, String.class));
+        MethodHandle collect = MethodHandles.collectArguments(target, 2, filter);
+        collect = MethodHandles.insertArguments(collect, 0, "a", "b", 'c', 'd');
+        MethodHandle mh = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkStringResult_delegate",
+            MethodType.methodType(void.class, MethodHandle.class, String.class));
+        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, collect, "a: a, b: b, c: c+d"));
+    }
+
+    public static void testFoldArgumentsMultiThreaded() throws Throwable {
+        MethodHandle target = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "foldTarget",
+            MethodType.methodType(String.class, String.class,
+                                  char.class, char.class, String.class));
+        MethodHandle filter = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "foldFilter",
+            MethodType.methodType(String.class, char.class, char.class));
+        MethodHandle adapter = MethodHandles.foldArguments(target, filter);
+        adapter = MethodHandles.insertArguments(adapter, 0, 'c', 'd', "e");
+        MethodHandle mh = MethodHandles.lookup().findStatic(
+            MethodHandleCombinersTest.class, "checkStringResult_delegate",
+            MethodType.methodType(void.class, MethodHandle.class, String.class));
+        invokeMultiThreaded(MethodHandles.insertArguments(mh, 0, adapter, "a: c+d ,b:c ,c:d ,d:e"));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
index 8381cbd..9b3850c 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
@@ -412,18 +412,26 @@
         public String bar();
     }
 
-    public static class BarSuper {
+    public static abstract class BarAbstractSuper {
+        public abstract String abstractSuperPublicMethod();
+    }
+
+    public static class BarSuper extends BarAbstractSuper {
         public String superPublicMethod() {
             return "superPublicMethod";
         }
 
-        public String superProtectedMethod() {
+        protected String superProtectedMethod() {
             return "superProtectedMethod";
         }
 
         String superPackageMethod() {
             return "superPackageMethod";
         }
+
+        public String abstractSuperPublicMethod() {
+            return "abstractSuperPublicMethod";
+        }
     }
 
     public static class BarImpl extends BarSuper implements Bar {
@@ -506,7 +514,6 @@
                     BarImpl.class, "add",
                     MethodType.methodType(String.class, Integer.class, int.class));
         } catch (NoSuchMethodException expected) {
-
         }
 
         // .. and their super-interfaces.
@@ -515,13 +522,16 @@
         str = (String) mh.invoke(new BarImpl());
         assertEquals("bar", str);
 
-        // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
-        // invoke resulting in a failing check in the interpreter.
-        //
-        // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
-        //    MethodType.methodType(String.class));
-        // str = (String) mh.invoke(new BarImpl());
-        // assertEquals("bar", str);
+
+        mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
+                                                MethodType.methodType(String.class));
+        str = (String) mh.invoke(new BarImpl());
+        assertEquals("bar", str);
+
+        mh = MethodHandles.lookup().findVirtual(BarAbstractSuper.class, "abstractSuperPublicMethod",
+                                                MethodType.methodType(String.class));
+        str = (String) mh.invoke(new BarImpl());
+        assertEquals("abstractSuperPublicMethod", str);
 
         // We should also be able to lookup public / protected / package methods in
         // the super class, given sufficient access privileges.
@@ -548,7 +558,7 @@
         }
     }
 
-    public static void testfindStatic() throws Throwable {
+    public void testfindStatic() throws Throwable {
         MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
                 MethodType.methodType(String.class));
         try {
@@ -580,7 +590,13 @@
         }
     }
 
-    static class UnreflectTester {
+    static class UnreflectTesterBase {
+        public String overridenMethod() {
+            return "Base";
+        }
+    }
+
+    static class UnreflectTester extends UnreflectTesterBase {
         public String publicField;
         private String privateField;
 
@@ -617,9 +633,16 @@
         public String publicVarArgsMethod(String... args) {
             return "publicVarArgsMethod";
         }
+
+        @Override
+        public String overridenMethod() {
+            return "Override";
+        }
+
+        public static final Lookup lookup = MethodHandles.lookup();
     }
 
-    public static void testUnreflects_publicMethods() throws Throwable {
+    public void testUnreflects_publicMethods() throws Throwable {
         UnreflectTester instance = new UnreflectTester("unused");
         Method publicMethod = UnreflectTester.class.getMethod("publicMethod");
 
@@ -633,7 +656,7 @@
         assertEquals("publicStaticMethod", (String) mh.invokeExact());
     }
 
-    public static void testUnreflects_privateMethods() throws Throwable {
+    public void testUnreflects_privateMethods() throws Throwable {
         Method privateMethod = UnreflectTester.class.getDeclaredMethod("privateMethod");
 
         try {
@@ -642,10 +665,14 @@
         } catch (IllegalAccessException expected) {
         }
 
-        privateMethod.setAccessible(true);
-
-        MethodHandle mh = MethodHandles.lookup().unreflect(privateMethod);
         UnreflectTester instance = new UnreflectTester("unused");
+        MethodHandle mh = UnreflectTester.lookup.unreflectSpecial(privateMethod,
+                UnreflectTester.class);
+        assertEquals("privateMethod", (String) mh.invoke(instance));
+        assertEquals("privateMethod", (String) mh.invokeExact(instance));
+
+        privateMethod.setAccessible(true);
+        mh = MethodHandles.lookup().unreflect(privateMethod);
         assertEquals("privateMethod", (String) mh.invoke(instance));
         assertEquals("privateMethod", (String) mh.invokeExact(instance));
 
@@ -656,13 +683,28 @@
         } catch (IllegalAccessException expected) {
         }
 
+        try {
+            mh = UnreflectTester.lookup.unreflectSpecial(privateStaticMethod,
+                    UnreflectTester.class);
+            fail();
+        } catch (IllegalAccessException expected) {
+        }
+
         privateStaticMethod.setAccessible(true);
         mh = MethodHandles.lookup().unreflect(privateStaticMethod);
         assertEquals("privateStaticMethod", (String) mh.invoke());
         assertEquals("privateStaticMethod", (String) mh.invokeExact());
     }
 
-    public static void testUnreflects_constructors() throws Throwable {
+    public void testUnreflectSpecial_superCalls() throws Throwable {
+        Method overridenMethod = UnreflectTester.class.getMethod("overridenMethod");
+        UnreflectTester instance = new UnreflectTester("unused");
+        MethodHandle mh = UnreflectTester.lookup.unreflectSpecial(overridenMethod,
+                UnreflectTester.class);
+        assertEquals("Base", (String) mh.invoke(instance));
+    }
+
+    public void testUnreflects_constructors() throws Throwable {
         Constructor privateConstructor = UnreflectTester.class.getDeclaredConstructor(String.class);
 
         try {
@@ -686,7 +728,7 @@
         assertEquals("def", instance.publicField);
     }
 
-    public static void testUnreflects_publicFields() throws Throwable {
+    public void testUnreflects_publicFields() throws Throwable {
         Field publicField = UnreflectTester.class.getField("publicField");
         MethodHandle mh = MethodHandles.lookup().unreflectGetter(publicField);
         UnreflectTester instance = new UnreflectTester("instanceValue");
@@ -708,7 +750,7 @@
         assertEquals("updatedStaticValue2", UnreflectTester.publicStaticField);
     }
 
-    public static void testUnreflects_privateFields() throws Throwable {
+    public void testUnreflects_privateFields() throws Throwable {
         Field privateField = UnreflectTester.class.getDeclaredField("privateField");
         try {
             MethodHandles.lookup().unreflectGetter(privateField);
@@ -760,7 +802,7 @@
         return "foo";
     }
 
-    public static void testAsType() throws Throwable {
+    public void testAsType() throws Throwable {
         // The type of this handle is (String, String)String.
         MethodHandle mh = MethodHandles.lookup().findVirtual(String.class,
                 "concat", MethodType.methodType(String.class, String.class));
@@ -803,7 +845,7 @@
         }
     }
 
-    public static void testConstructors() throws Throwable {
+    public void testConstructors() throws Throwable {
         MethodHandle mh =
                 MethodHandles.lookup().findConstructor(Float.class,
                         MethodType.methodType(void.class,
@@ -872,7 +914,7 @@
         }
     }
 
-    public static void testStringConstructors() throws Throwable {
+    public void testStringConstructors() throws Throwable {
         final String testPattern = "The system as we know it is broken";
 
         // String()
@@ -965,7 +1007,7 @@
         assertEquals(testPattern, s);
     }
 
-    private static void testReferenceReturnValueConversions() throws Throwable {
+    public void testReferenceReturnValueConversions() throws Throwable {
         MethodHandle mh = MethodHandles.lookup().findStatic(
                 Float.class, "valueOf", MethodType.methodType(Float.class, String.class));
 
@@ -1023,7 +1065,7 @@
         assertEquals(0, c.compareTo(Float.valueOf(2.125f)));
     }
 
-    private static void testPrimitiveReturnValueConversions() throws Throwable {
+    public void testPrimitiveReturnValueConversions() throws Throwable {
         MethodHandle mh = MethodHandles.lookup().findStatic(
                 Math.class, "min", MethodType.methodType(int.class, int.class, int.class));
 
@@ -1155,11 +1197,6 @@
         }
     }
 
-    public static void testReturnValueConversions() throws Throwable {
-        testReferenceReturnValueConversions();
-        testPrimitiveReturnValueConversions();
-    }
-
     public static class BaseVariableArityTester {
         public String update(Float f0, Float... floats) {
             return "base " + f0 + ", " + Arrays.toString(floats);
@@ -1860,6 +1897,72 @@
         assertEquals(MethodType.methodType(String.class), info.getMethodType());
     }
 
+    public void testReflectAs() throws Throwable {
+        // Test with a virtual method :
+        MethodType type = MethodType.methodType(String.class);
+        MethodHandle handle = MethodHandles.lookup().findVirtual(
+                UnreflectTester.class, "publicMethod", type);
+
+        Method reflected = MethodHandles.reflectAs(Method.class, handle);
+        Method meth = UnreflectTester.class.getMethod("publicMethod");
+        assertEquals(meth, reflected);
+
+        try {
+            MethodHandles.reflectAs(Field.class, handle);
+            fail();
+        } catch (ClassCastException expected) {
+        }
+
+        try {
+            MethodHandles.reflectAs(Constructor.class, handle);
+            fail();
+        } catch (ClassCastException expected) {
+        }
+
+        // Test with a private instance method, unlike the "checked crack" (lol..) API exposed
+        // by revealDirect, this doesn't perform any access checks.
+        handle = UnreflectTester.lookup.findSpecial(
+                UnreflectTester.class, "privateMethod", type, UnreflectTester.class);
+        meth = UnreflectTester.class.getDeclaredMethod("privateMethod");
+        reflected = MethodHandles.reflectAs(Method.class, handle);
+        assertEquals(meth, reflected);
+
+        // Test with a constructor :
+        type = MethodType.methodType(void.class, String.class, boolean.class);
+        handle = MethodHandles.lookup().findConstructor(UnreflectTester.class, type);
+
+        Constructor cons = UnreflectTester.class.getConstructor(String.class, boolean.class);
+        Constructor reflectedCons = MethodHandles.reflectAs(Constructor.class, handle);
+        assertEquals(cons, reflectedCons);
+
+        try {
+            MethodHandles.reflectAs(Method.class, handle);
+            fail();
+        } catch (ClassCastException expected) {
+        }
+
+        // Test with an instance field :
+        handle = MethodHandles.lookup().findSetter(
+                UnreflectTester.class, "publicField", String.class);
+
+        Field field = UnreflectTester.class.getField("publicField");
+        Field reflectedField = MethodHandles.reflectAs(Field.class, handle);
+        assertEquals(field, reflectedField);
+
+        try {
+            MethodHandles.reflectAs(Method.class, handle);
+            fail();
+        } catch (ClassCastException expected) {
+        }
+
+        // Test with a non-direct method handle.
+        try {
+            MethodHandles.reflectAs(Method.class, MethodHandles.constant(String.class, "foo"));
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
     public static class Inner1 {
         public static MethodHandles.Lookup lookup = MethodHandles.lookup();
     }
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index f8a905e..73c6ea3 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -16,24 +16,35 @@
 
 package libcore.java.net;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
-import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
 import libcore.util.SerializationTester;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class InetAddressTest extends junit.framework.TestCase {
+@RunWith(JUnitParamsRunner.class)
+public class InetAddressTest {
     private static final byte[] LOOPBACK4_BYTES = new byte[] { 127, 0, 0, 1 };
     private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
 
-    private static final String[] INVALID_IPv4_NUMERIC_ADDRESSES = new String[] {
+    private static final String[] INVALID_IPv4_AND_6_NUMERIC_ADDRESSES = new String[] {
         // IPv4 addresses may not be surrounded by square brackets.
         "[127.0.0.1]",
 
@@ -55,6 +66,9 @@
         "1234",
         "0", // Single out the deprecated form of the ANY address.
 
+        // Older Harmony tests expected this to be resolved to 255.255.255.255.
+        "4294967295", // 0xffffffffL,
+
         // Hex. Not supported by Android but supported by the RI.
         "0x1.0x2.0x3.0x4",
         "0x7f.0x00.0x00.0x01",
@@ -71,6 +85,25 @@
         "1.-1.0.1",
         "1.0.-1.1",
         "1.0.0.-1",
+
+        // Invalid IPv6 addresses
+        "FFFF:FFFF",
+    };
+
+    private static final String VALID_IPv6_ADDRESSES[] = {
+        "::1.2.3.4",
+        "::",
+        "::",
+        "1::0",
+        "1::",
+        "::1",
+        "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
+        "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255",
+        "0:0:0:0:0:0:0:0",
+        "0:0:0:0:0:0:0.0.0.0",
+        "::255.255.255.255",
+        "::FFFF:0.0.0.0",
+        "F:F:F:F:F:F:F:F",
     };
 
     private static Inet6Address loopback6() throws Exception {
@@ -81,66 +114,96 @@
         return (Inet6Address) InetAddress.getByAddress("ip6-localhost", LOOPBACK6_BYTES);
     }
 
-    public void test_parseNumericAddress() throws Exception {
-        // Regular IPv4.
-        assertEquals("/1.2.3.4", InetAddress.parseNumericAddress("1.2.3.4").toString());
-        // Regular IPv6.
-        assertEquals("/2001:4860:800d::68", InetAddress.parseNumericAddress("2001:4860:800d::68").toString());
-        // Mapped IPv4
-        assertEquals("/127.0.0.1", InetAddress.parseNumericAddress("::ffff:127.0.0.1").toString());
-        // Optional square brackets around IPv6 addresses, including mapped IPv4.
-        assertEquals("/2001:4860:800d::68", InetAddress.parseNumericAddress("[2001:4860:800d::68]").toString());
-        assertEquals("/127.0.0.1", InetAddress.parseNumericAddress("[::ffff:127.0.0.1]").toString());
+    public static String[][] validNumericAddressesAndStringRepresentation() {
+        return new String[][]{
+            // Regular IPv4.
+            { "1.2.3.4", "/1.2.3.4" },
 
+            // Regular IPv6.
+            { "2001:4860:800d::68", "/2001:4860:800d::68" },
+
+            // Mapped IPv4
+            { "::ffff:127.0.0.1", "/127.0.0.1" },
+
+            // Optional square brackets around IPv6 addresses, including mapped IPv4.
+            { "[2001:4860:800d::68]", "/2001:4860:800d::68" },
+            { "[::ffff:127.0.0.1]", "/127.0.0.1" },
+
+            // Android does not recognize Octal (leading 0) cases: they are treated as decimal.
+            { "0177.00.00.01", "/177.0.0.1" },
+        };
+    }
+
+    @Parameters(method = "validNumericAddressesAndStringRepresentation")
+    @Test
+    public void test_parseNumericAddress(String address, String expectedString) throws Exception {
+        assertEquals(expectedString, InetAddress.parseNumericAddress(address).toString());
+    }
+
+    @Test
+    public void test_parseNumericAddress_notNumeric() throws Exception {
         try {
             InetAddress.parseNumericAddress("example.com"); // Not numeric.
             fail();
         } catch (IllegalArgumentException expected) {
         }
 
-        // Android does not recognize Octal (leading 0) cases: they are treated as decimal.
-        assertEquals("/177.0.0.1", InetAddress.parseNumericAddress("0177.00.00.01").toString());
-
-        for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) {
-            try {
-                InetAddress.parseNumericAddress(invalid);
-                fail(invalid);
-            } catch (IllegalArgumentException expected) {
-            }
-        }
-
         // Strange special cases, for compatibility with InetAddress.getByName.
         assertTrue(InetAddress.parseNumericAddress(null).isLoopbackAddress());
         assertTrue(InetAddress.parseNumericAddress("").isLoopbackAddress());
     }
 
-    public void test_isNumeric() throws Exception {
-        // IPv4
-        assertTrue(InetAddress.isNumeric("1.2.3.4"));
-        assertTrue(InetAddress.isNumeric("127.0.0.1"));
-
-        // IPv6
-        assertTrue(InetAddress.isNumeric("::1"));
-        assertTrue(InetAddress.isNumeric("2001:4860:800d::68"));
-
-        // Mapped IPv4
-        assertTrue(InetAddress.isNumeric("::ffff:127.0.0.1"));
-
-        // Optional square brackets around IPv6 addresses, including mapped IPv4.
-        assertTrue(InetAddress.isNumeric("[2001:4860:800d::68]"));
-        assertTrue(InetAddress.isNumeric("[::ffff:127.0.0.1]"));
-
-        // Negative test
-        assertFalse(InetAddress.isNumeric("example.com"));
-
-        // Android does not handle Octal (leading 0) cases: they are treated as decimal.
-        assertTrue(InetAddress.isNumeric("0177.00.00.01")); // Interpreted as 177.0.0.1
-
-        for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) {
-            assertFalse(invalid, InetAddress.isNumeric(invalid));
+    @Parameters(method = "invalidNumericAddresses")
+    @Test
+    public void test_parseNumericAddress_invalid(String invalid) throws Exception {
+        try {
+            InetAddress.parseNumericAddress(invalid);
+            fail(invalid);
+        } catch (IllegalArgumentException expected) {
         }
     }
 
+    public static String[] validNumericAddresses() {
+        return new String[] {
+            // IPv4
+            "1.2.3.4",
+            "127.0.0.1",
+
+            // IPv6
+            "::1",
+            "2001:4860:800d::68",
+
+            // Mapped IPv4
+            "::ffff:127.0.0.1",
+
+            // Optional square brackets around IPv6 addresses, including mapped IPv4.
+            "[2001:4860:800d::68]",
+            "[::ffff:127.0.0.1]",
+
+            // Android does not handle Octal (leading 0) cases: they are treated as decimal.
+            "0177.00.00.01",
+        };
+    }
+
+    @Parameters(method = "validNumericAddresses")
+    @Test
+    public void test_isNumeric(String valid) throws Exception {
+        assertTrue(InetAddress.isNumeric(valid));
+    }
+
+    @Test
+    public void test_isNumeric_notNumeric() throws Exception {
+        // Negative test
+        assertFalse(InetAddress.isNumeric("example.com"));
+    }
+
+    @Parameters(method = "invalidNumericAddresses")
+    @Test
+    public void test_isNumeric_invalid(String invalid) {
+        assertFalse(invalid, InetAddress.isNumeric(invalid));
+    }
+
+    @Test
     public void test_isLinkLocalAddress() throws Exception {
         assertFalse(InetAddress.getByName("127.0.0.1").isLinkLocalAddress());
         assertFalse(InetAddress.getByName("::ffff:127.0.0.1").isLinkLocalAddress());
@@ -150,6 +213,7 @@
         assertTrue(InetAddress.getByName("fe80::").isLinkLocalAddress());
     }
 
+    @Test
     public void test_isMCSiteLocalAddress() throws Exception {
         assertFalse(InetAddress.getByName("239.254.255.255").isMCSiteLocal());
         assertTrue(InetAddress.getByName("239.255.0.0").isMCSiteLocal());
@@ -161,6 +225,7 @@
         assertTrue(InetAddress.getByName("ff15::").isMCSiteLocal());
     }
 
+    @Test
     public void test_isReachable() throws Exception {
         // http://code.google.com/p/android/issues/detail?id=20203
         String s = "aced0005737200146a6176612e6e65742e496e6574416464726573732d9b57af"
@@ -182,6 +247,7 @@
         }.test();
     }
 
+    @Test
     public void test_isReachable_neverThrows() throws Exception {
         InetAddress inetAddress = InetAddress.getByName("www.google.com");
 
@@ -197,6 +263,7 @@
     // IPPROTO_ICMP socket kind requires setting ping_group_range. This is set on boot on Android.
     // When running on host, make sure you run the command:
     //   sudo sysctl -w net.ipv4.ping_group_range="0 65535"
+    @Test
     public void test_isReachable_by_ICMP() throws Exception {
         InetAddress[] inetAddresses = InetAddress.getAllByName("www.google.com");
         for (InetAddress ia : inetAddresses) {
@@ -209,6 +276,7 @@
         assertFalse(blackholeAddress.isReachable(1000));
     }
 
+    @Test
     public void test_isSiteLocalAddress() throws Exception {
         assertFalse(InetAddress.getByName("144.32.32.1").isSiteLocalAddress());
         assertTrue(InetAddress.getByName("10.0.0.1").isSiteLocalAddress());
@@ -220,20 +288,53 @@
         assertTrue(InetAddress.getByName("fec0::").isSiteLocalAddress());
     }
 
-    public void test_getByName() throws Exception {
-        for (String invalid : INVALID_IPv4_NUMERIC_ADDRESSES) {
-            try {
-                InetAddress.getByName(invalid);
-                fail(invalid);
-            } catch (UnknownHostException expected) {
-            }
+    public static String[] invalidNumericAddresses() {
+        return INVALID_IPv4_AND_6_NUMERIC_ADDRESSES;
+    }
+
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    @Parameters(method = "invalidNumericAddresses")
+    @Test
+    public void test_getByName_invalid(String invalid) throws Exception {
+        try {
+            InetAddress.getByName(invalid);
+            fail("Invalid IP address incorrectly recognized as valid: "
+                + invalid);
+        } catch (UnknownHostException expected) {
+        }
+
+        // exercise negative cache
+        try {
+            InetAddress.getByName(invalid);
+            fail("Invalid IP address incorrectly recognized as valid: "
+                + invalid);
+        } catch (Exception expected) {
         }
     }
 
+    public static String[] validIPv6Addresses() {
+        return VALID_IPv6_ADDRESSES;
+    }
+
+    @Parameters(method = "validIPv6Addresses")
+    @Test
+    public void test_getByName_valid(String valid) throws Exception {
+        InetAddress.getByName(valid);
+
+        // exercise positive cache
+        InetAddress.getByName(valid);
+
+        // when wrapped in [..]
+        String tempIPAddress = "[" + valid + "]";
+        InetAddress.getByName(tempIPAddress);
+    }
+
+    @Test
     public void test_getLoopbackAddress() throws Exception {
         assertTrue(InetAddress.getLoopbackAddress().isLoopbackAddress());
     }
 
+    @Test
     public void test_equals() throws Exception {
         InetAddress addr = InetAddress.getByName("239.191.255.255");
         assertTrue(addr.equals(addr));
@@ -247,6 +348,7 @@
         assertEquals(Inet6Address.getByAddress("1", bs, 1), Inet6Address.getByAddress("2", bs, 2));
     }
 
+    @Test
     public void test_getHostAddress() throws Exception {
         assertEquals("::1", localhost6().getHostAddress());
         assertEquals("::1", InetAddress.getByName("::1").getHostAddress());
@@ -314,6 +416,7 @@
         assertEquals("10:2030:4050:6070:8090:a0b0:c0d0:e0f0", aAddr.getHostAddress());
     }
 
+    @Test
     public void test_hashCode() throws Exception {
         InetAddress addr1 = InetAddress.getByName("1.0.0.1");
         InetAddress addr2 = InetAddress.getByName("1.0.0.1");
@@ -322,29 +425,30 @@
         assertTrue(loopback6().hashCode() == localhost6().hashCode());
     }
 
-    public void test_toString() throws Exception {
-        String validIPAddresses[] = {
-            "::1.2.3.4", "::", "::", "1::0", "1::", "::1",
-            "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
-            "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255",
-            "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0.0.0.0"
+    public static String[][] validAddressesAndStringRepresentation() {
+        return new String[][] {
+            { "::1.2.3.4", "/::1.2.3.4" },
+            { "::", "/::" },
+            { "1::0", "/1::" },
+            { "1::", "/1::" },
+            { "::1", "/::1" },
+            { "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" },
+            { "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255", "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" },
+            { "0:0:0:0:0:0:0:0", "/::" },
+            { "0:0:0:0:0:0:0.0.0.0", "/::" },
         };
-
-        String [] resultStrings = {
-            "/::1.2.3.4", "/::", "/::", "/1::", "/1::", "/::1",
-            "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
-            "/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "/::",
-            "/::"
-        };
-
-        for(int i = 0; i < validIPAddresses.length; i++) {
-            InetAddress ia = InetAddress.getByName(validIPAddresses[i]);
-            String result = ia.toString();
-            assertNotNull(result);
-            assertEquals(resultStrings[i], result);
-        }
     }
 
+    @Parameters(method = "validAddressesAndStringRepresentation")
+    @Test
+    public void test_toString(String address, String expectedString) throws Exception {
+        InetAddress ia = InetAddress.getByName(address);
+        String result = ia.toString();
+        assertNotNull(result);
+        assertEquals(expectedString, result);
+    }
+
+    @Test
     public void test_getHostNameCaches() throws Exception {
         InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
 
@@ -358,52 +462,60 @@
         assertEquals("ip6-localhost", getHostStringWithoutReverseDns(inetAddress));
     }
 
+    @Test
     public void test_getByAddress_loopbackIpv4() throws Exception {
         InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK4_BYTES);
-        assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+        checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
         assertTrue(inetAddress.isLoopbackAddress());
     }
 
+    @Test
     public void test_getByAddress_loopbackIpv6() throws Exception {
         InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
-        assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+        checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
         assertTrue(inetAddress.isLoopbackAddress());
     }
 
+    @Test
     public void test_getByName_loopbackIpv4() throws Exception {
         InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
-        assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+        checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
         assertTrue(inetAddress.isLoopbackAddress());
     }
 
+    @Test
     public void test_getByName_loopbackIpv6() throws Exception {
         InetAddress inetAddress = InetAddress.getByName("::1");
-        assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+        checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
         assertTrue(inetAddress.isLoopbackAddress());
     }
 
+    @Test
     public void test_getByName_empty() throws Exception {
         InetAddress inetAddress = InetAddress.getByName("");
-        assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+        checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
         assertTrue(inetAddress.isLoopbackAddress());
     }
 
+    @Test
     public void test_getAllByName_localhost() throws Exception {
         InetAddress[] inetAddresses = InetAddress.getAllByName("localhost");
         assertEquals(1, inetAddresses.length);
         InetAddress inetAddress = inetAddresses[0];
-        assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+        checkInetAddress(LOOPBACK4_BYTES, "localhost", inetAddress);
         assertTrue(inetAddress.isLoopbackAddress());
     }
 
+    @Test
     public void test_getAllByName_ip6_localhost() throws Exception {
         InetAddress[] inetAddresses = InetAddress.getAllByName("ip6-localhost");
         assertEquals(1, inetAddresses.length);
         InetAddress inetAddress = inetAddresses[0];
-        assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+        checkInetAddress(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
         assertTrue(inetAddress.isLoopbackAddress());
     }
 
+    @Test
     public void test_getByName_v6loopback() throws Exception {
         InetAddress inetAddress = InetAddress.getByName("::1");
 
@@ -412,6 +524,7 @@
         assertTrue(expectedLoopbackAddresses.contains(inetAddress));
     }
 
+    @Test
     public void test_getByName_cloning() throws Exception {
         InetAddress[] addresses = InetAddress.getAllByName(null);
         InetAddress[] addresses2 = InetAddress.getAllByName(null);
@@ -427,6 +540,7 @@
         assertNotNull(addresses2[1]);
     }
 
+    @Test
     public void test_getAllByName_null() throws Exception {
         InetAddress[] inetAddresses = InetAddress.getAllByName(null);
         assertEquals(2, inetAddresses.length);
@@ -436,13 +550,14 @@
     }
 
     // http://b/29311351
+    @Test
     public void test_loopbackConstantsPreInitializedNames() {
         // Note: Inet6Address / Inet4Address equals() does not check host name.
         assertEquals("ip6-localhost", getHostStringWithoutReverseDns(Inet6Address.LOOPBACK));
         assertEquals("localhost", getHostStringWithoutReverseDns(Inet4Address.LOOPBACK));
     }
 
-    private static void assertEquals(
+    private static void checkInetAddress(
         byte[] expectedAddressBytes, String expectedHostname, InetAddress actual) {
         assertArrayEquals(expectedAddressBytes, actual.getAddress());
         assertEquals(expectedHostname, actual.getHostName());
diff --git a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
index b21689e..9515fcc 100644
--- a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
+++ b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
@@ -96,16 +96,17 @@
             fail("Abort: " + e);
             throw new AssertionError("unreachable");
         }
-        final CountDownLatch shutdownLatch = new CountDownLatch(1);
-        ServerRunnable serverRunnable = new ServerRunnable(serverSocket, shutdownLatch);
+        ServerRunnable serverRunnable = new ServerRunnable(serverSocket);
         Thread serverThread = new Thread(serverRunnable, TAG + " (server)");
         ClientRunnable clientRunnable = new ClientRunnable(
-                serverSocket.getLocalSocketAddress(), shutdownLatch);
+                serverSocket.getLocalSocketAddress(), serverRunnable);
         Thread clientThread = new Thread(clientRunnable, TAG + " (client)");
         serverThread.start();
         clientThread.start();
         try {
-            if (shutdownLatch.getCount() == 0) {
+            assertTrue("Slow server startup", serverRunnable.awaitStart(1, TimeUnit.SECONDS));
+            assertTrue("Slow client startup", clientRunnable.awaitStart(1, TimeUnit.SECONDS));
+            if (serverRunnable.isShutdown()) {
                 fail("Server prematurely shut down");
             }
             // Let server and client keep connecting for some time, then close the socket.
@@ -117,9 +118,8 @@
             }
             // Check that the server shut down quickly in response to the socket closing.
             long hardLimitSeconds = 5;
-            boolean serverShutdownReached = shutdownLatch.await(hardLimitSeconds, TimeUnit.SECONDS);
+            boolean serverShutdownReached = serverRunnable.awaitShutdown(hardLimitSeconds, TimeUnit.SECONDS);
             if (!serverShutdownReached) { // b/27763633
-                shutdownLatch.countDown();
                 String serverStackTrace = stackTraceAsString(serverThread.getStackTrace());
                 fail("Server took > " + hardLimitSeconds + "sec to react to serverSocket.close(). "
                         + "Server thread's stackTrace: " + serverStackTrace);
@@ -134,7 +134,7 @@
                     iterationName, msecPerIteration),
                     serverRunnable.numSuccessfulConnections > 0);
 
-            assertEquals(0, shutdownLatch.getCount());
+            assertTrue(serverRunnable.isShutdown());
             // Sanity check to ensure the threads don't live into the next iteration. This should
             // be quick because we only get here if shutdownLatch reached 0 within the time limit.
             serverThread.join();
@@ -151,17 +151,20 @@
      */
     static class ClientRunnable implements Runnable {
         private final SocketAddress socketAddress;
-        private final CountDownLatch shutdownLatch;
+
+        private final ServerRunnable serverRunnable;
+        private final CountDownLatch startLatch = new CountDownLatch(1);
 
         public ClientRunnable(
-                SocketAddress socketAddress, CountDownLatch shutdownLatch) {
+                SocketAddress socketAddress, ServerRunnable serverRunnable) {
             this.socketAddress = socketAddress;
-            this.shutdownLatch = shutdownLatch;
+            this.serverRunnable = serverRunnable;
         }
 
         @Override
         public void run() {
-            while (shutdownLatch.getCount() != 0) { // check if server is shutting down
+            startLatch.countDown();
+            while (!serverRunnable.isShutdown()) {
                 try {
                     Socket socket = new Socket();
                     socket.connect(socketAddress, /* timeout (msec) */ 10);
@@ -171,6 +174,11 @@
                 }
             }
         }
+
+        public boolean awaitStart(long timeout, TimeUnit timeUnit) throws InterruptedException {
+            return startLatch.await(timeout, timeUnit);
+        }
+
     }
 
     /**
@@ -180,15 +188,16 @@
     static class ServerRunnable implements Runnable {
         private final ServerSocket serverSocket;
         volatile int numSuccessfulConnections;
-        private final CountDownLatch shutdownLatch;
+        private final CountDownLatch startLatch = new CountDownLatch(1);
+        private final CountDownLatch shutdownLatch = new CountDownLatch(1);
 
-        ServerRunnable(ServerSocket serverSocket, CountDownLatch shutdownLatch) {
+        ServerRunnable(ServerSocket serverSocket) {
             this.serverSocket = serverSocket;
-            this.shutdownLatch = shutdownLatch;
         }
 
         @Override
         public void run() {
+            startLatch.countDown();
             int numSuccessfulConnections = 0;
             while (true) {
                 try {
@@ -204,6 +213,18 @@
                 }
             }
         }
+
+        public boolean awaitStart(long timeout, TimeUnit timeUnit) throws InterruptedException {
+            return startLatch.await(timeout, timeUnit);
+        }
+
+        public boolean awaitShutdown(long timeout, TimeUnit timeUnit) throws InterruptedException {
+            return shutdownLatch.await(timeout, timeUnit);
+        }
+
+        public boolean isShutdown() {
+            return shutdownLatch.getCount() == 0;
+        }
     }
 
     private static String stackTraceAsString(StackTraceElement[] stackTraceElements) {
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index c80d2ec..750e73a 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -158,7 +158,20 @@
         // allow (but strip) trailing \n, \r and \r\n
         // assertForbiddenRequestHeaderValue("\r");
         // End of workaround
-        assertEquals("a valid\tvalue", setAndReturnRequestHeaderValue("a valid\tvalue"));
+
+        // '\t' in header values can either be (a) forbidden or (b) allowed.
+        // The original version of Android N (API 23) implemented behavior
+        // (a), but OEMs can backport a fix that changes the behavior to (b).
+        // Therefore, this test has been relaxed for Android N CTS to allow
+        // either behavior. It is planned that future versions of Android only
+        // allow behavior (b).
+        try {
+            // throws IAE in case (a), passes in case (b)
+            assertEquals("a valid\tvalue", setAndReturnRequestHeaderValue("a valid\tvalue"));
+        } catch (IllegalArgumentException tolerated) {
+            // verify case (a)
+            assertForbiddenRequestHeaderValue("\t");
+        }
         assertForbiddenRequestHeaderValue("\u001f");
         assertForbiddenRequestHeaderValue("\u007f");
 
diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java
index 9063e27..c54d978 100644
--- a/luni/src/test/java/libcore/java/nio/BufferTest.java
+++ b/luni/src/test/java/libcore/java/nio/BufferTest.java
@@ -955,7 +955,7 @@
         ByteBuffer b2 = b1.duplicate();
         NioUtils.freeDirectBuffer(b1);
         for (ByteBuffer b: new ByteBuffer[] { b1, b2 }) {
-          //assertFalse(b.isAccessible());
+          assertFalse(b.isAccessible());
             try {
                 b.compact();
                 fail();
@@ -973,7 +973,6 @@
         }
     }
 
-    /* setAccessible is not available in OpenJdk's buffers.
     public void testAccess() {
         ByteBuffer b1 = ByteBuffer.allocate(1);
         ByteBuffer b2 = b1.duplicate();
@@ -1057,7 +1056,7 @@
             testAsMethods(b);
             testGetMethods(b);
         }
-        }*/
+    }
 
     private void testPutMethods(ByteBuffer b) {
         b.position(0);
diff --git a/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java
new file mode 100644
index 0000000..905f120
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/AsynchronousFileChannelTest.java
@@ -0,0 +1,683 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.nio.channels;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousFileChannel;
+import java.nio.channels.CompletionHandler;
+import java.nio.channels.NonReadableChannelException;
+import java.nio.channels.NonWritableChannelException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.*;
+
+@RunWith(JUnit4.class)
+public class AsynchronousFileChannelTest {
+
+    @Test
+    public void testOpen_create() throws Throwable {
+        Path tempDir = Files.createTempDirectory("ASFCTest_test_open_create");
+
+        Path newFile = tempDir.resolve("newFile");
+        AsynchronousFileChannel channel = AsynchronousFileChannel.open(newFile,
+                StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+        assertTrue(channel.isOpen());
+        assertEquals(0, channel.size());
+        channel.close();
+    }
+
+    @Test
+    public void testOpen_existing() throws Throwable {
+        // Demonstrates a few warts related to re-opening files that already exist.
+        Path tempDir = Files.createTempDirectory("ASFCTest_test_open_existing");
+        Path newFile = tempDir.resolve("newFile");
+
+        // Create a new file.
+        AsynchronousFileChannel channel = AsynchronousFileChannel.open(newFile,
+                StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+        channel.close();
+
+        // This should fail, but it doesn't..
+        AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE_NEW);
+        // ..unless it's paired with a write.
+        try {
+            AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE_NEW,
+                    StandardOpenOption.WRITE);
+            fail();
+        } catch (FileAlreadyExistsException expected) {
+        }
+
+        // Create should still work, though.
+        channel = AsynchronousFileChannel.open(newFile, StandardOpenOption.CREATE,
+                StandardOpenOption.WRITE);
+        channel.close();
+    }
+
+    @Test
+    public void testOpen_nonexistent() throws Throwable {
+        Path tempDir = Files.createTempDirectory("ASFCTest_test_open_nonexistent");
+
+        Path nonExistent = tempDir.resolve("nonExistentFile");
+        try {
+            AsynchronousFileChannel.open(nonExistent, StandardOpenOption.READ);
+            fail();
+        } catch (NoSuchFileException expected) {
+        }
+
+        try {
+            AsynchronousFileChannel.open(nonExistent, StandardOpenOption.WRITE);
+            fail();
+        } catch (NoSuchFileException expected) {
+        }
+
+        // The following three cases haven't clearly been mentioned in the documentation.
+        // CREATE / CREATE_NEW fail unless they're paired with WRITE.
+        //
+        // CREATE will succeed without WRITE in the case that the file already exists, which
+        // seems like a wart.
+        try {
+            AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE);
+            fail();
+        } catch (NoSuchFileException expected) {
+        }
+
+        try {
+            AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE,
+                    StandardOpenOption.READ);
+            fail();
+        } catch (NoSuchFileException expected) {
+        }
+
+        try {
+            AsynchronousFileChannel.open(nonExistent, StandardOpenOption.CREATE_NEW);
+            fail();
+        } catch (NoSuchFileException expected) {
+        }
+    }
+
+    private static File createTemporaryFile(int size) throws IOException {
+        if (size % 256 != 0) {
+            throw new IllegalArgumentException("size % 256 != 0: " + size);
+        }
+
+        File temp = File.createTempFile("AFCTest_tempfile", "");
+        byte[] buf = new byte[256];
+
+        try (FileOutputStream fos = new FileOutputStream(temp)) {
+            int bytesWritten = 0;
+            while (bytesWritten < size) {
+                fos.write(buf);
+                bytesWritten += buf.length;
+            }
+        }
+
+        return temp;
+    }
+
+    private static File createTemporaryFile(byte[] contents) throws IOException {
+        File temp = File.createTempFile("AFCTest_tempfile", "");
+
+        try (FileOutputStream fos = new FileOutputStream(temp)) {
+            fos.write(contents);
+        }
+
+        return temp;
+    }
+
+    @Test
+    public void testOpen_truncate() throws Throwable {
+        File temp = createTemporaryFile(256);
+        assertEquals(256, temp.length());
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
+        afc.close();
+
+        assertEquals(0, temp.length());
+    }
+
+    @Test
+    public void testOpen_deleteOnClose() throws Throwable {
+        File temp = createTemporaryFile(256);
+        assertTrue(temp.exists());
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.READ);
+        assertEquals(256, afc.size());
+        afc.close();
+
+        assertFalse(temp.exists());
+    }
+
+    @Test
+    public void testRead_Future() throws Throwable {
+        byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+        File temp = createTemporaryFile(contents);
+
+        byte[] readBuf = new byte[4];
+        ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.WRITE);
+        try {
+            afc.read(buf, 0);
+            fail();
+        } catch (NonReadableChannelException exception) {
+        }
+
+        afc.close();
+
+        afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.READ);
+
+        Future<Integer> fut = afc.read(buf, 0);
+        assertEquals(4, (int) fut.get());
+        buf.flip();
+        assertEquals('a', readBuf[0]);
+        assertEquals('b', readBuf[1]);
+        assertEquals('c', readBuf[2]);
+        assertEquals('d', readBuf[3]);
+
+        // Short read: at the end of the file.
+        fut = afc.read(buf, 6);
+        assertEquals(2, (int) fut.get());
+        assertEquals('g', readBuf[0]);
+        assertEquals('h', readBuf[1]);
+
+        // Reads past the end of the file.
+        fut = afc.read(buf, 8);
+        assertEquals(-1, (int) fut.get());
+
+        fut = afc.read(buf, 9);
+        assertEquals(-1, (int) fut.get());
+
+        try {
+            afc.read(buf, -1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            afc.read(null, 1);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            afc.read(buf.asReadOnlyBuffer(), 1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        afc.close();
+    }
+
+    static class RecordingHandler implements CompletionHandler<Integer, String> {
+        public String attachment;
+        public int result;
+        public Throwable exc;
+
+        private final CountDownLatch cdl = new CountDownLatch(1);
+
+        @Override
+        public void completed(Integer result, String attachment) {
+            this.result = result;
+            this.attachment = attachment;
+
+            cdl.countDown();
+        }
+
+        @Override
+        public void failed(Throwable exc, String attachment) {
+            this.exc = exc;
+            this.attachment = attachment;
+        }
+
+        public boolean awaitCompletion() throws InterruptedException {
+            return cdl.await(10, TimeUnit.SECONDS);
+        }
+    }
+
+    @Test
+    public void testRead_CompletionListener() throws Throwable {
+        byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+        File temp = createTemporaryFile(contents);
+
+        byte[] readBuf = new byte[4];
+        ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.WRITE);
+        String attachment = "ATTACHMENT";
+        RecordingHandler handler = new RecordingHandler();
+
+        try {
+            afc.read(buf, 0, attachment, handler);
+            fail();
+        } catch (NonReadableChannelException exception) {
+        }
+
+        afc.close();
+
+        afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.READ);
+        afc.read(buf, 0, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertEquals(4, handler.result);
+        assertSame(attachment, handler.attachment);
+        buf.flip();
+        assertEquals('a', readBuf[0]);
+        assertEquals('b', readBuf[1]);
+        assertEquals('c', readBuf[2]);
+        assertEquals('d', readBuf[3]);
+
+        // Short read: at the end of the file.
+        handler = new RecordingHandler();
+        attachment = "ATTACHMENT2";
+        afc.read(buf, 6, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertEquals(2, handler.result);
+        assertSame(attachment, handler.attachment);
+        assertEquals('g', readBuf[0]);
+        assertEquals('h', readBuf[1]);
+
+        // Reads past the end of the file.
+        handler = new RecordingHandler();
+        attachment = "ATTACHMENT3";
+        afc.read(buf, 8, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertEquals(-1, handler.result);
+        assertSame(attachment, handler.attachment);
+
+        handler = new RecordingHandler();
+        attachment = "ATTACHMENT4";
+        afc.read(buf, 9, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertEquals(-1, handler.result);
+        assertSame(attachment, handler.attachment);
+
+        handler = new RecordingHandler();
+        attachment = "ATTACHMENT5";
+        try {
+            afc.read(buf, -1, attachment, handler);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            afc.read(null, 1, attachment, handler);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            afc.read(buf.asReadOnlyBuffer(), 1, attachment, handler);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        afc.close();
+    }
+
+    @Test
+    public void testWrite_Future() throws Throwable {
+        byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+        File temp = createTemporaryFile(contents);
+
+        byte[] readBuf = new byte[4];
+        ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.READ);
+        try {
+            afc.write(buf, 0);
+            fail();
+        } catch (NonWritableChannelException exception) {
+        }
+
+        afc.close();
+
+        afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.WRITE, StandardOpenOption.READ);
+
+        assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0).get());
+
+        Future<Integer> fut = afc.read(buf, 0);
+
+        assertEquals(4, (int) fut.get());
+        buf.flip();
+        assertEquals('x', readBuf[0]);
+        assertEquals('y', readBuf[1]);
+        assertEquals('c', readBuf[2]);
+        assertEquals('d', readBuf[3]);
+
+        // Write that expands beyond the end of the file.
+        assertEquals(3, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y', 'z'}), 6).get());
+        assertEquals(9, afc.size());
+
+        buf.rewind();
+        fut = afc.read(buf, 6);
+        assertEquals(3, (int) fut.get());
+        buf.flip();
+        assertEquals('x', readBuf[0]);
+        assertEquals('y', readBuf[1]);
+        assertEquals('z', readBuf[2]);
+
+        // Writes at the end of the file.
+        assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y' }), 9).get());
+        assertEquals(11, afc.size());
+        buf.rewind();
+        fut = afc.read(buf, 9);
+        assertEquals(2, (int) fut.get());
+        buf.flip();
+        assertEquals('x', readBuf[0]);
+        assertEquals('y', readBuf[1]);
+
+        // Writes past the end of the file.
+        assertEquals(2, (int) afc.write(ByteBuffer.wrap(new byte[] { '0', '2' }), 13).get());
+        assertEquals(15, afc.size());
+
+        // This is broken behaviour. At this point, there are 4 readable bytes in the file
+        // and our buffer should be filled with {0, 0, 48, 50}...
+        buf.rewind();
+        fut = afc.read(buf, 11);
+        assertEquals(2, (int) fut.get());
+        buf.flip();
+        assertEquals(0, readBuf[0]);
+        assertEquals(0, readBuf[1]);
+
+        // ... if we explicitly read at position 13, things are somehow fine again.
+        buf.rewind();
+        fut = afc.read(buf, 13);
+        assertEquals(2, (int) fut.get());
+        buf.flip();
+        assertEquals('0', readBuf[0]);
+        assertEquals('2', readBuf[1]);
+
+        try {
+            afc.write(buf, -1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            afc.write(null, 1);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        afc.close();
+    }
+
+    @Test
+    public void testWrite_CompletionListener() throws Throwable {
+        byte[] contents = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+        File temp = createTemporaryFile(contents);
+
+        byte[] readBuf = new byte[4];
+        ByteBuffer buf = ByteBuffer.wrap(readBuf);
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.READ);
+
+        String attachment = "ATTACHMENT";
+        RecordingHandler handler = new RecordingHandler();
+        try {
+            afc.write(buf, 0, attachment, handler);
+            fail();
+        } catch (NonWritableChannelException exception) {
+        }
+
+        afc.close();
+
+        afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.WRITE, StandardOpenOption.READ);
+
+        attachment = "ATTACHMENT";
+        handler = new RecordingHandler();
+        afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y'}), 0, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertSame(attachment, handler.attachment);
+        assertEquals(2, handler.result);
+
+        Future<Integer> fut = afc.read(buf, 0);
+
+        assertEquals(4, (int) fut.get());
+        buf.flip();
+        assertEquals('x', readBuf[0]);
+        assertEquals('y', readBuf[1]);
+        assertEquals('c', readBuf[2]);
+        assertEquals('d', readBuf[3]);
+
+        // Write that expands beyond the end of the file.
+        attachment = "ATTACHMENT2";
+        handler = new RecordingHandler();
+        afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y', 'z'}), 6, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertSame(attachment, handler.attachment);
+        assertEquals(3, handler.result);
+
+        assertEquals(9, afc.size());
+
+        buf.rewind();
+        fut = afc.read(buf, 6);
+        assertEquals(3, (int) fut.get());
+        buf.flip();
+        assertEquals('x', readBuf[0]);
+        assertEquals('y', readBuf[1]);
+        assertEquals('z', readBuf[2]);
+
+        // Writes at the end of the file.
+        attachment = "ATTACHMENT3";
+        handler = new RecordingHandler();
+        afc.write(ByteBuffer.wrap(new byte[] { 'x', 'y' }), 9, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertSame(attachment, handler.attachment);
+        assertEquals(2, handler.result);
+
+        assertEquals(11, afc.size());
+        buf.rewind();
+        fut = afc.read(buf, 9);
+        assertEquals(2, (int) fut.get());
+        buf.flip();
+        assertEquals('x', readBuf[0]);
+        assertEquals('y', readBuf[1]);
+
+        // Writes past the end of the file.
+        attachment = "ATTACHMENT4";
+        handler = new RecordingHandler();
+        afc.write(ByteBuffer.wrap(new byte[] { '0', '2' }), 13, attachment, handler);
+        assertTrue(handler.awaitCompletion());
+        assertSame(attachment, handler.attachment);
+        assertEquals(2, handler.result);
+
+        assertEquals(15, afc.size());
+
+        // This is broken behaviour. At this point, there are 4 readable bytes in the file
+        // and our buffer should be filled with {0, 0, 48, 50}...
+        buf.rewind();
+        fut = afc.read(buf, 11);
+        assertEquals(2, (int) fut.get());
+        buf.flip();
+        assertEquals(0, readBuf[0]);
+        assertEquals(0, readBuf[1]);
+
+        // ... if we explicitly read at position 13, things are somehow fine again.
+        buf.rewind();
+        fut = afc.read(buf, 13);
+        assertEquals(2, (int) fut.get());
+        buf.flip();
+        assertEquals('0', readBuf[0]);
+        assertEquals('2', readBuf[1]);
+
+        try {
+            afc.write(buf, -1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            afc.write(null, 1);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        afc.close();
+    }
+
+    @Test
+    public void testWrite_Append() throws Throwable {
+        File temp = createTemporaryFile(256);
+
+        try {
+            AsynchronousFileChannel.open(temp.toPath(),
+                    StandardOpenOption.WRITE, StandardOpenOption.APPEND);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+
+    @Test
+    public void testSize() throws Throwable {
+        File temp = createTemporaryFile(256);
+        assertEquals(256, temp.length());
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.WRITE);
+        // Test initial size.
+        assertEquals(256, afc.size());
+
+        // Test that the size is updated after a write at the end of the file.
+        ByteBuffer buf = ByteBuffer.allocate(16);
+        assertEquals(16, (int) afc.write(buf, 256).get());
+        assertEquals(272, afc.size());
+
+        // Test that the size is updated after a truncate.
+        afc.truncate(16);
+        assertEquals(16, afc.size());
+        afc.close();
+    }
+
+    @Test
+    public void testTruncate() throws Throwable {
+        File temp = createTemporaryFile(256);
+        assertEquals(256, temp.length());
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(temp.toPath(),
+                StandardOpenOption.WRITE);
+        afc.truncate(128);
+        assertEquals(128, afc.size());
+        assertEquals(128, temp.length());
+
+        afc.truncate(0);
+        assertEquals(0, afc.size());
+        assertEquals(0, temp.length());
+
+        // Should be a no-op if the length is greater than the current length.
+        afc.truncate(128);
+        assertEquals(0, afc.size());
+        assertEquals(0, temp.length());
+
+        try {
+            afc.truncate(-1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        afc.close();
+
+        // Attempts to truncate a file that's not writeable should throw a NWCE.
+        temp = createTemporaryFile(256);
+        afc = AsynchronousFileChannel.open(temp.toPath(), StandardOpenOption.READ);
+        try {
+            afc.truncate(128);
+            fail();
+        } catch (NonWritableChannelException expected) {
+        }
+
+        try {
+            afc.truncate(384);
+            fail();
+        } catch (NonWritableChannelException expected) {
+        }
+
+        afc.close();
+    }
+
+    @Test
+    public void testCustomExecutor() throws Throwable {
+        AtomicReference<Thread> serviceThread = new AtomicReference<>();
+        ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                serviceThread.set(new Thread(r));
+                return serviceThread.get();
+            }
+        });
+
+        Set<OpenOption> openOptions = new HashSet<>();
+        openOptions.add(StandardOpenOption.READ);
+        openOptions.add(StandardOpenOption.WRITE);
+
+        AsynchronousFileChannel afc = AsynchronousFileChannel.open(
+                Files.createTempFile("AFCTest_testCustomExecutor", ""),
+                openOptions, executorService);
+
+        final LinkedBlockingQueue<Thread> observedThreads = new LinkedBlockingQueue<>();
+        CompletionHandler<Integer, String> handler = new CompletionHandler<Integer, String>() {
+            @Override
+            public void completed(Integer result, String attachment) {
+                assertTrue(observedThreads.offer(Thread.currentThread()));
+            }
+
+            @Override
+            public void failed(Throwable exc, String attachment) {
+                assertTrue(observedThreads.offer(Thread.currentThread()));
+            }
+        };
+
+        afc.write(ByteBuffer.allocate(16), 0, "foo", handler);
+        assertSame(serviceThread.get(), observedThreads.take());
+        assertEquals(0, observedThreads.size());
+
+        afc.read(ByteBuffer.allocate(16), 0, "foo", handler);
+        assertSame(serviceThread.get(), observedThreads.take());
+        assertEquals(0, observedThreads.size());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java b/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java
index 4bbf3a9..c867250 100644
--- a/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/ChannelsTest.java
@@ -17,12 +17,25 @@
 package libcore.java.nio.channels;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousByteChannel;
 import java.nio.channels.Channels;
 import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.Pipe;
 import java.nio.channels.WritableByteChannel;
+import java.util.Arrays;
+import java.util.concurrent.Future;
 import junit.framework.TestCase;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 
 public final class ChannelsTest extends TestCase {
 
@@ -56,5 +69,60 @@
         sourceChannel.configureBlocking(false);
         return sourceChannel;
     }
-}
 
+    public void testInputStreamAsynchronousByteChannel() throws Exception {
+        AsynchronousByteChannel abc = mock(AsynchronousByteChannel.class);
+        InputStream is = Channels.newInputStream(abc);
+        Future<Integer> result = mock(Future.class);
+        ArgumentCaptor<ByteBuffer> bbCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
+        final byte[] bytesRead = new byte[10];
+
+        when(abc.read(bbCaptor.capture())).thenReturn(result);
+        when(result.get()).thenAnswer(
+            new Answer<Integer>() {
+                public Integer answer(InvocationOnMock invocation) {
+                    ByteBuffer bb = bbCaptor.getValue();
+                    assertEquals(bytesRead.length, bb.remaining());
+                    // Write '7' bytes
+                    bb.put(new byte[] {0, 1, 2, 3, 4, 5, 6});
+                    return 7;
+                }
+            });
+
+        assertEquals(7, is.read(bytesRead));
+        // Only 7 bytes of data should be written into the buffer
+        byte[] bytesExpected = new byte[] { 0, 1, 2, 3, 4, 5, 6, 0, 0, 0 };
+        assertTrue(Arrays.equals(bytesExpected, bytesRead));
+
+        Mockito.verify(abc).read(isA(ByteBuffer.class));
+        Mockito.verify(result).get();
+    }
+
+    public void testOutputStreamAsynchronousByteChannel() throws Exception {
+        AsynchronousByteChannel abc = mock(AsynchronousByteChannel.class);
+        OutputStream os = Channels.newOutputStream(abc);
+        Future<Integer> result = mock(Future.class);
+        ArgumentCaptor<ByteBuffer> bbCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
+        final byte[] data = "world".getBytes();
+
+        when(abc.write(bbCaptor.capture())).thenReturn(result);
+        when(result.get()).thenAnswer(
+            new Answer<Integer>() {
+                public Integer answer(InvocationOnMock invocation) {
+                    ByteBuffer bb = bbCaptor.getValue();
+                    assertEquals(data.length, bb.remaining());
+                    byte[] readData = new byte[data.length];
+                    // Read the whole thing
+                    bb.get(readData);
+                    assertTrue(Arrays.equals(data, readData));
+                    return data.length;
+                }
+            });
+
+        os.write(data);
+
+        Mockito.verify(abc).write(isA(ByteBuffer.class));
+        Mockito.verify(result).get();
+  }
+
+}
diff --git a/luni/src/test/java/libcore/java/nio/file/Files2Test.java b/luni/src/test/java/libcore/java/nio/file/Files2Test.java
index 44c4ec9..e23ae6a 100644
--- a/luni/src/test/java/libcore/java/nio/file/Files2Test.java
+++ b/luni/src/test/java/libcore/java/nio/file/Files2Test.java
@@ -23,7 +23,19 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.NonReadableChannelException;
+import java.nio.channels.NonWritableChannelException;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.MalformedInputException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.AccessDeniedException;
 import java.nio.file.CopyOption;
 import java.nio.file.FileStore;
 import java.nio.file.FileSystem;
@@ -34,6 +46,8 @@
 import java.nio.file.Files;
 import java.nio.file.LinkOption;
 import java.nio.file.NoSuchFileException;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.BasicFileAttributes;
@@ -44,22 +58,40 @@
 import java.nio.file.attribute.PosixFilePermissions;
 import java.nio.file.spi.FileSystemProvider;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import sun.security.action.GetPropertyAction;
 
 import static java.nio.file.FileVisitResult.CONTINUE;
 import static java.nio.file.FileVisitResult.TERMINATE;
+import static java.nio.file.StandardOpenOption.APPEND;
+import static java.nio.file.StandardOpenOption.CREATE_NEW;
+import static java.nio.file.StandardOpenOption.READ;
+import static java.nio.file.StandardOpenOption.SYNC;
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static java.nio.file.StandardOpenOption.WRITE;
+import static java.security.AccessController.doPrivileged;
 import static junit.framework.TestCase.assertTrue;
 import static libcore.java.nio.file.FilesSetup.DATA_FILE;
 import static libcore.java.nio.file.FilesSetup.NON_EXISTENT_FILE;
+import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA;
+import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA_2;
+import static libcore.java.nio.file.FilesSetup.UTF_16_DATA;
 import static libcore.java.nio.file.FilesSetup.execCmdAndWaitForTermination;
+import static libcore.java.nio.file.FilesSetup.readFromFile;
 import static libcore.java.nio.file.FilesSetup.readFromInputStream;
+import static libcore.java.nio.file.FilesSetup.writeToFile;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -243,11 +275,6 @@
     }
 
     @Test
-    public void test_setOwner() throws IOException {
-        // TODO: unable to set the owner due to insufficient permissions.
-    }
-
-    @Test
     public void test_isSymbolicLink() throws IOException, InterruptedException {
         assertFalse(Files.isSymbolicLink(filesSetup.getTestPath()));
         assertFalse(Files.isSymbolicLink(filesSetup.getDataFilePath()));
@@ -794,6 +821,987 @@
         } catch (FileSystemLoopException expected) {}
     }
 
+    @Test
+    public void test_find() throws IOException {
+        // Directory structure.
+        //        root
+        //        ├── dir1
+        //        │   ├── dir2
+        //        │   │   ├── dir3
+        //        │   │   └── file5
+        //        │   ├── dir4
+        //        │   └── file3
+        //        ├── dir5
+        //        └── file1
+        //
+
+        // Directory setup.
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+        Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+        Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2");
+        Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3");
+        Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4");
+        Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5");
+        Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+        Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3");
+        Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5");
+
+        Files.createDirectories(dir3);
+        Files.createDirectories(dir4);
+        Files.createDirectories(dir5);
+        Files.createFile(file1);
+        Files.createFile(file3);
+        Files.createFile(file5);
+
+        // When depth is 2 then file4, file5 and dir3 are not reachable.
+        Set<Path> expectedDirSet = new HashSet<>();
+        expectedDirSet.add(rootDir);
+        expectedDirSet.add(dir1);
+        expectedDirSet.add(dir2);
+        expectedDirSet.add(dir4);
+        expectedDirSet.add(dir5);
+        Set<Path> dirSet = new HashSet<>();
+        Stream<Path> pathStream = Files.find(rootDir, 2, (path, attr) -> Files.isDirectory(path));
+        pathStream.forEach(path -> dirSet.add(path));
+        assertEquals(expectedDirSet, dirSet);
+
+        // Test the case where depth is 0.
+        expectedDirSet.clear();
+        dirSet.clear();
+
+        expectedDirSet.add(rootDir);
+
+        pathStream = Files.find(rootDir, 0, (path, attr) -> Files.isDirectory(path));
+        pathStream.forEach(path -> dirSet.add(path));
+        assertEquals(expectedDirSet, dirSet);
+
+        // Test the case where depth is -1.
+        try {
+            Files.find(rootDir, -1, (path, attr) -> Files.isDirectory(path));
+            fail();
+        } catch (IllegalArgumentException expected) {}
+
+        // Test the case when BiPredicate always returns false.
+        expectedDirSet.clear();
+        dirSet.clear();
+
+        pathStream = Files.find(rootDir, 2, (path, attr) -> false);
+        pathStream.forEach(path -> dirSet.add(path));
+        assertEquals(expectedDirSet, dirSet);
+
+        // Test the case when start is not a directory.
+        expectedDirSet.clear();
+        dirSet.clear();
+
+        expectedDirSet.add(file1);
+
+        pathStream = Files.find(file1, 2, (path, attr) -> true);
+        pathStream.forEach(path -> dirSet.add(path));
+        assertEquals(expectedDirSet, dirSet);
+    }
+
+    @Test
+    public void test_find_NPE() throws IOException {
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+        Files.createDirectories(rootDir);
+        try {
+            Files.find(null, 2, (path, attr) -> Files.isDirectory(path));
+            fail();
+        } catch(NullPointerException expected) {}
+
+        try {
+            Files.find(rootDir, (Integer)null, (path, attr) -> Files.isDirectory(path));
+            fail();
+        } catch(NullPointerException expected) {}
+
+        try(Stream<Path> pathStream = Files.find(rootDir, 2, null)) {
+            pathStream.forEach(path -> {/* do nothing */});
+            fail();
+        } catch(NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_lines$Path$Charset() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add(UTF_16_DATA);
+        lines.add(TEST_FILE_DATA);
+        Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_16);
+        try (Stream<String> readLines = Files.lines(filesSetup.getDataFilePath(),
+                StandardCharsets.UTF_16)) {
+            Iterator<String> lineIterator = lines.iterator();
+            readLines.forEach(line -> assertEquals(line, lineIterator.next()));
+        }
+
+        // When Path is a directory
+        filesSetup.reset();
+        try (Stream<String> readLines = Files.lines(filesSetup.getTestDirPath(),
+                StandardCharsets.UTF_16)) {
+            try {
+                readLines.count();
+                fail();
+            } catch (UncheckedIOException expected) {}
+        }
+
+        // When file doesn't exits.
+        filesSetup.reset();
+        try (Stream<String> readLines = Files.lines(filesSetup.getTestPath(),
+                StandardCharsets.UTF_16)) {
+           fail();
+        } catch (NoSuchFileException expected) {}
+    }
+
+    @Test
+    public void test_lines$Path$Charset_NPE() throws IOException {
+        try {
+            Files.lines(null, StandardCharsets.UTF_16);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.lines(filesSetup.getDataFilePath(), null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_lines$Path() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add(TEST_FILE_DATA_2);
+        lines.add(TEST_FILE_DATA);
+        Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_8);
+        try (Stream<String> readLines = Files.lines(filesSetup.getDataFilePath())) {
+            Iterator<String> lineIterator = lines.iterator();
+            readLines.forEach(line -> assertEquals(line, lineIterator.next()));
+        }
+
+        // When Path is a directory
+        filesSetup.reset();
+        try (Stream<String> readLines = Files.lines(filesSetup.getTestDirPath())) {
+            try {
+                readLines.count();
+                fail();
+            } catch (UncheckedIOException expected) {}
+        }
+
+        // When file doesn't exits.
+        filesSetup.reset();
+        try (Stream<String> readLines = Files.lines(filesSetup.getTestPath())) {
+            fail();
+        } catch (NoSuchFileException expected) {}
+    }
+
+    @Test
+    public void test_line$Path_NPE() throws IOException {
+        try {
+            Files.lines(null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_list() throws Exception {
+        // Directory Setup for the test.
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+        Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+        Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+        Path file2 = Paths.get(filesSetup.getTestDir(), "root/dir1/file2");
+        Path symLink = Paths.get(filesSetup.getTestDir(), "root/symlink");
+        Files.createDirectories(dir1);
+        Files.createFile(file1);
+        Files.createFile(file2);
+        Files.createSymbolicLink(symLink, file1.toAbsolutePath());
+
+        Set<Path> expectedVisitedFiles = new HashSet<>();
+        expectedVisitedFiles.add(dir1);
+        expectedVisitedFiles.add(file1);
+        expectedVisitedFiles.add(symLink);
+
+        Set<Path> visitedFiles = new HashSet<>();
+        try (Stream<Path> pathStream = Files.list(rootDir)) {
+            pathStream.forEach(path -> visitedFiles.add(path));
+        }
+        assertEquals(3, visitedFiles.size());
+
+
+        // Test the case where directory is empty.
+        filesSetup.clearAll();
+        try {
+            Files.list(Paths.get(filesSetup.getTestDir(), "newDir"));
+            fail();
+        } catch (NoSuchFileException expected) {}
+
+        // Test the case where path points to a file.
+        filesSetup.clearAll();
+        filesSetup.setUp();
+        try {
+            Files.list(filesSetup.getDataFilePath());
+            fail();
+        } catch (NotDirectoryException expected) {}
+    }
+
+    @Test
+    public void test_list_NPE() throws IOException {
+        try {
+            Files.list(null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_newBufferedReader() throws IOException {
+        // Test the case where file doesn't exists.
+        try {
+            Files.newBufferedReader(filesSetup.getTestPath());
+            fail();
+        } catch (NoSuchFileException expected) {}
+
+        BufferedReader bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath());
+        assertEquals(TEST_FILE_DATA, bufferedReader.readLine());
+
+        // Test the case where the file content has unicode characters.
+        writeToFile(filesSetup.getDataFilePath(), UTF_16_DATA);
+        bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath());
+        assertEquals(UTF_16_DATA, bufferedReader.readLine());
+        bufferedReader.close();
+
+        // Test the case where file is write-only.
+        Set<PosixFilePermission> perm = PosixFilePermissions.fromString("-w-------");
+        Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm);
+        try {
+            Files.newBufferedReader(filesSetup.getDataFilePath());
+            fail();
+        } catch (AccessDeniedException expected) {}
+    }
+
+    @Test
+    public void test_newBufferedReader_NPE() throws IOException {
+        try {
+            Files.newBufferedReader(null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_newBufferedReader$Path$Charset() throws IOException {
+        BufferedReader bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath(),
+                StandardCharsets.US_ASCII);
+        assertEquals(TEST_FILE_DATA, bufferedReader.readLine());
+
+        // When the file has unicode characters.
+        writeToFile(filesSetup.getDataFilePath(), UTF_16_DATA);
+        bufferedReader = Files.newBufferedReader(filesSetup.getDataFilePath(),
+                StandardCharsets.US_ASCII);
+        try {
+            bufferedReader.readLine();
+            fail();
+        } catch (MalformedInputException expected) {}
+    }
+
+    @Test
+    public void test_newBufferedReader$Path$Charset_NPE() throws IOException {
+        try {
+            Files.newBufferedReader(null, StandardCharsets.US_ASCII);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.newBufferedReader(filesSetup.getDataFilePath(), null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_newBufferedWriter() throws IOException {
+        BufferedWriter bufferedWriter = Files.newBufferedWriter(filesSetup.getTestPath());
+        bufferedWriter.write(TEST_FILE_DATA);
+        bufferedWriter.close();
+        assertEquals(TEST_FILE_DATA,
+                readFromFile(filesSetup.getTestPath()));
+
+        // When file exists, it should start writing from the beginning.
+        bufferedWriter = Files.newBufferedWriter(filesSetup.getDataFilePath());
+        bufferedWriter.write(TEST_FILE_DATA_2);
+        bufferedWriter.close();
+        assertEquals(TEST_FILE_DATA_2,
+                readFromFile(filesSetup.getDataFilePath()));
+
+        // When file is read-only.
+        Set<PosixFilePermission> perm = PosixFilePermissions.fromString("r--------");
+        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+        Files.setPosixFilePermissions(filesSetup.getDataFilePath(), perm);
+        try {
+            Files.newBufferedWriter(filesSetup.getDataFilePath());
+            fail();
+        } catch (AccessDeniedException expected) {}
+    }
+
+    @Test
+    public void test_newBufferedWriter_NPE() throws IOException {
+        try {
+            Files.newBufferedWriter(null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_newBufferedWriter$Path$Charset() throws IOException {
+        BufferedWriter bufferedWriter = Files.newBufferedWriter(filesSetup.getTestPath(),
+                StandardCharsets.US_ASCII);
+        bufferedWriter.write(TEST_FILE_DATA);
+        bufferedWriter.close();
+        assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath()));
+    }
+
+    @Test
+    public void test_newBufferedWriter$Path$Charset_NPE() throws IOException {
+        try {
+            Files.newBufferedWriter(null, StandardCharsets.US_ASCII);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.newBufferedWriter(filesSetup.getTestPath(), (OpenOption[]) null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_newByteChannel() throws IOException {
+        // When file doesn't exist
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getTestPath())) {
+            fail();
+        } catch (NoSuchFileException expected) {
+        }
+
+        // When file exists.
+
+        // File opens in READ mode by default. The channel is non writable by default.
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath())) {
+            sbc.write(ByteBuffer.allocate(10));
+            fail();
+        } catch (NonWritableChannelException expected) {
+        }
+
+        // Read a file.
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath())) {
+            ByteBuffer readBuffer = ByteBuffer.allocate(10);
+            int bytesReadCount = sbc.read(readBuffer);
+
+            String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount),
+                    StandardCharsets.UTF_8);
+            assertEquals(TEST_FILE_DATA, readData);
+        }
+    }
+
+    @Test
+    public void test_newByteChannel_openOption_WRITE() throws IOException {
+        // When file doesn't exist
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getTestPath(), WRITE)) {
+            fail();
+        } catch (NoSuchFileException expected) {
+        }
+
+        // When file exists.
+
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE)) {
+            sbc.read(ByteBuffer.allocate(10));
+            fail();
+        } catch (NonReadableChannelException expected) {
+        }
+
+        // Write in file.
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE)) {
+            sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
+            sbc.close();
+
+            try (InputStream is = Files.newInputStream(filesSetup.getDataFilePath())) {
+                String expectedFileData = TEST_FILE_DATA_2 +
+                        TEST_FILE_DATA.substring(
+                                TEST_FILE_DATA_2.length());
+                assertEquals(expectedFileData, readFromInputStream(is));
+            }
+        }
+    }
+
+    @Test
+    public void test_newByteChannel_openOption_WRITE_READ() throws IOException {
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(), WRITE,
+                READ, SYNC/* Sync makes sure the that InputStream is able to read content written by
+                 the seekable byte channel without closing/flushing it. */)) {
+            ByteBuffer readBuffer = ByteBuffer.allocate(10);
+            int bytesReadCount = sbc.read(readBuffer);
+
+            String readData = new String(Arrays.copyOf(readBuffer.array(), bytesReadCount),
+                    StandardCharsets.UTF_8);
+            assertEquals(TEST_FILE_DATA, readData);
+
+            // Pointer will move to the end of the file after read operation. The write should
+            // append the data at the end of the file.
+            sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
+            try (InputStream is = Files.newInputStream(filesSetup.getDataFilePath())) {
+                String expectedFileData = TEST_FILE_DATA + TEST_FILE_DATA_2;
+                assertEquals(expectedFileData, readFromInputStream(is));
+            }
+        }
+    }
+
+    @Test
+    public void test_newByteChannel_NPE() throws IOException {
+        try (SeekableByteChannel sbc = Files.newByteChannel(null)) {
+            fail();
+        } catch(NullPointerException expected) {}
+
+        try (SeekableByteChannel sbc = Files.newByteChannel(filesSetup.getDataFilePath(),
+                (OpenOption[]) null)) {
+            fail();
+        } catch(NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_readAllLine() throws IOException {
+        // Multi-line file.
+        assertTrue(Files.exists(filesSetup.getDataFilePath()));
+        writeToFile(filesSetup.getDataFilePath(), "\n" + TEST_FILE_DATA_2,
+                APPEND);
+        List<String> out = Files.readAllLines(filesSetup.getDataFilePath());
+        assertEquals(2, out.size());
+        assertEquals(TEST_FILE_DATA, out.get(0));
+        assertEquals(TEST_FILE_DATA_2, out.get(1));
+
+        // When file doesn't exist.
+        filesSetup.reset();
+        try {
+            Files.readAllLines(filesSetup.getTestPath());
+            fail();
+        } catch (NoSuchFileException expected) {}
+
+        // When file is a directory.
+        filesSetup.reset();
+        try {
+            Files.readAllLines(filesSetup.getTestDirPath());
+            fail();
+        } catch (IOException expected) {}
+    }
+
+    @Test
+    public void test_readAllLine_NPE() throws IOException {
+        try {
+            Files.readAllLines(null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_readAllLine$Path$Charset() throws IOException {
+        assertTrue(Files.exists(filesSetup.getDataFilePath()));
+        writeToFile(filesSetup.getDataFilePath(), "\n" + TEST_FILE_DATA_2, APPEND);
+        List<String> out = Files.readAllLines(filesSetup.getDataFilePath(), StandardCharsets.UTF_8);
+        assertEquals(2, out.size());
+        assertEquals(TEST_FILE_DATA, out.get(0));
+        assertEquals(TEST_FILE_DATA_2, out.get(1));
+
+        // With UTF-16.
+        out = Files.readAllLines(filesSetup.getDataFilePath(), StandardCharsets.UTF_16);
+        assertEquals(1, out.size());
+
+        // UTF-8 data read as UTF-16
+        String expectedOutput = new String((TEST_FILE_DATA + '\n' + TEST_FILE_DATA_2).getBytes(),
+                StandardCharsets.UTF_16);
+        assertEquals(expectedOutput, out.get(0));
+
+        // When file doesn't exist.
+        filesSetup.reset();
+        try {
+            Files.readAllLines(filesSetup.getTestPath(), StandardCharsets.UTF_16);
+            fail();
+        } catch (NoSuchFileException expected) {}
+
+        // When file is a directory.
+        filesSetup.reset();
+        try {
+            Files.readAllLines(filesSetup.getTestDirPath(), StandardCharsets.UTF_16);
+            fail();
+        } catch (IOException expected) {}
+    }
+
+    @Test
+    public void test_readAllLine$Path$Charset_NPE() throws IOException {
+        try {
+            Files.readAllLines(null, StandardCharsets.UTF_16);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.readAllLines(filesSetup.getDataFilePath(), null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_walk$Path$FileVisitOption() throws IOException {
+        // Directory structure.
+        //        root
+        //        ├── dir1
+        //        │   ├── dir2
+        //        │   │   ├── dir3
+        //        │   │   └── file5
+        //        │   ├── dir4
+        //        │   └── file3
+        //        ├── dir5
+        //        └── file1
+        //
+        // depth will be 2. file4, file5, dir3 is not reachable.
+
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+        Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+        Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2");
+        Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3");
+        Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4");
+        Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5");
+        Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+        Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3");
+        Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5");
+
+        Files.createDirectories(dir3);
+        Files.createDirectories(dir4);
+        Files.createDirectories(dir5);
+        Files.createFile(file1);
+        Files.createFile(file3);
+        Files.createFile(file5);
+
+        Set<Path> expectedDirSet = new HashSet<>();
+        expectedDirSet.add(rootDir);
+        expectedDirSet.add(dir1);
+        expectedDirSet.add(dir2);
+        expectedDirSet.add(dir4);
+        expectedDirSet.add(file3);
+        expectedDirSet.add(dir5);
+        expectedDirSet.add(file1);
+
+        Set<Path> dirSet = new HashSet<>();
+        try(Stream<Path> pathStream = Files.walk(rootDir, 2,
+                FileVisitOption.FOLLOW_LINKS)) {
+            pathStream.forEach(path -> dirSet.add(path));
+        }
+
+        assertEquals(expectedDirSet, dirSet);
+
+        // Test case when Path doesn't exist.
+        try (Stream<Path> pathStream = Files.walk(filesSetup.getTestPath(), 2,
+                FileVisitOption.FOLLOW_LINKS)){
+            fail();
+        } catch (NoSuchFileException expected) {}
+
+        // Test case when Path is a not a directory.
+        expectedDirSet.clear();
+        dirSet.clear();
+        expectedDirSet.add(filesSetup.getDataFilePath());
+        try (Stream<Path> pathStream = Files.walk(filesSetup.getDataFilePath(), 2,
+                FileVisitOption.FOLLOW_LINKS)){
+            pathStream.forEach(path -> dirSet.add(path));
+        }
+        assertEquals(expectedDirSet, dirSet);
+
+        // Test case when Path doesn't exist.
+        try (Stream<Path> pathStream = Files.walk(rootDir, -1, FileVisitOption.FOLLOW_LINKS)){
+            fail();
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    @Test
+    public void test_walk_FileSystemLoopException() throws IOException {
+        // Directory structure.
+        //        root
+        //        └── dir1
+        //            └── file1
+        //
+        // file1 is symbolic link to dir1
+
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+        Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir");
+        Path file1 = Paths.get(filesSetup.getTestDir(), "root/dir/file1");
+        Files.createDirectories(dir1);
+        Files.createSymbolicLink(file1, dir1.toAbsolutePath());
+        assertTrue(Files.isSymbolicLink(file1));
+        try(Stream<Path> pathStream = Files.walk(rootDir, FileVisitOption.FOLLOW_LINKS)) {
+            pathStream.forEach(path -> assertNotNull(path));
+            fail();
+        } catch (UncheckedIOException expected) {
+            assertTrue(expected.getCause() instanceof FileSystemLoopException);
+        }
+    }
+
+    @Test
+    public void test_walk() throws IOException {
+        // Directory structure.
+        //        root
+        //        ├── dir1
+        //        │   ├── dir2
+        //        │   │   ├── dir3
+        //        │   │   └── file5
+        //        │   ├── dir4
+        //        │   └── file3
+        //        ├── dir5
+        //        └── file1
+        //
+
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+        Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+        Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2");
+        Path dir3 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/dir3");
+        Path dir4 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir4");
+        Path dir5 = Paths.get(filesSetup.getTestDir(), "root/dir5");
+        Path file1 = Paths.get(filesSetup.getTestDir(), "root/file1");
+        Path file3 = Paths.get(filesSetup.getTestDir(), "root/dir1/file3");
+        Path file5 = Paths.get(filesSetup.getTestDir(), "root/dir1/dir2/file5");
+
+        Files.createDirectories(dir3);
+        Files.createDirectories(dir4);
+        Files.createDirectories(dir5);
+        Files.createFile(file1);
+        Files.createFile(file3);
+        Files.createFile(file5);
+
+        Set<Path> expectedDirSet = new HashSet<>();
+        expectedDirSet.add(rootDir.getFileName());
+        expectedDirSet.add(dir1.getFileName());
+        expectedDirSet.add(dir2.getFileName());
+        expectedDirSet.add(dir4.getFileName());
+        expectedDirSet.add(file3.getFileName());
+        expectedDirSet.add(dir5.getFileName());
+        expectedDirSet.add(file1.getFileName());
+        expectedDirSet.add(file5.getFileName());
+        expectedDirSet.add(dir3.getFileName());
+
+        Set<Path> dirSet = new HashSet<>();
+        try (Stream<Path> pathStream = Files.walk(rootDir)) {
+            pathStream.forEach(path -> dirSet.add(path.getFileName()));
+        }
+
+        assertEquals(expectedDirSet, dirSet);
+
+
+        // Test case when Path doesn't exist.
+        try (Stream<Path> pathStream = Files.walk(filesSetup.getTestPath())){
+            fail();
+        } catch (NoSuchFileException expected) {}
+
+        // Test case when Path is a not a directory.
+        expectedDirSet.clear();
+        dirSet.clear();
+        expectedDirSet.add(filesSetup.getDataFilePath());
+        try (Stream<Path> pathStream = Files.walk(filesSetup.getDataFilePath())) {
+            pathStream.forEach(path -> dirSet.add(path));
+        }
+        assertEquals(expectedDirSet, dirSet);
+    }
+
+    @Test
+    public void test_walk_depthFirst() throws IOException {
+        // Directory structure.
+        //        root
+        //        ├── dir1
+        //        │   └── file1
+        //        └── dir2
+        //            └── file2
+        //
+
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "root");
+        Path dir1 = Paths.get(filesSetup.getTestDir(), "root/dir1");
+        Path file1 = Paths.get(filesSetup.getTestDir(), "root/dir1/file1");
+        Path dir2 = Paths.get(filesSetup.getTestDir(), "root/dir2");
+        Path file2 = Paths.get(filesSetup.getTestDir(), "root/dir2/file2");
+        Files.createDirectories(dir1);
+        Files.createDirectories(dir2);
+        Files.createFile(file1);
+        Files.createFile(file2);
+        List<Object> fileKeyList = new ArrayList<>();
+        try(Stream<Path> pathStream = Files.walk(rootDir, FileVisitOption.FOLLOW_LINKS)) {
+            pathStream.forEach(path -> fileKeyList.add(path.getFileName()));
+        }
+        assertEquals(rootDir.getFileName(), fileKeyList.get(0));
+        if (fileKeyList.get(1).equals(dir1.getFileName())) {
+            assertEquals(file1.getFileName(), fileKeyList.get(2));
+            assertEquals(dir2.getFileName(), fileKeyList.get(3));
+            assertEquals(file2.getFileName(), fileKeyList.get(4));
+        } else if (fileKeyList.get(1).equals(dir2.getFileName())) {
+            assertEquals(file2.getFileName(), fileKeyList.get(2));
+            assertEquals(dir1.getFileName(), fileKeyList.get(3));
+            assertEquals(file1.getFileName(), fileKeyList.get(4));
+        } else {
+            fail();
+        }
+    }
+
+    @Test
+    public void test_walk$Path$Int$LinkOption_IllegalArgumentException() throws IOException {
+        Map<Path, Boolean> dirMap = new HashMap<>();
+        Path rootDir = Paths.get(filesSetup.getTestDir(), "rootDir");
+        try (Stream<Path> pathStream = Files.walk(rootDir, -1,
+                FileVisitOption.FOLLOW_LINKS)) {
+            fail();
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    @Test
+    public void test_walk$Path$FileVisitOption_NPE() throws IOException {
+        try {
+            Files.walk(null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_write$Path$byte$OpenOption() throws IOException {
+        Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes());
+        assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath()));
+    }
+
+    @Test
+    public void test_write$Path$byte$OpenOption_OpenOption() throws IOException {
+        Files.write(filesSetup.getTestPath(), TEST_FILE_DATA_2.getBytes(), CREATE_NEW);
+        assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getTestPath()));
+
+        filesSetup.reset();
+        Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), TRUNCATE_EXISTING);
+        assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getDataFilePath()));
+
+        filesSetup.reset();
+        Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), APPEND);
+        assertEquals(TEST_FILE_DATA + TEST_FILE_DATA_2, readFromFile(
+                filesSetup.getDataFilePath()));
+
+        filesSetup.reset();
+        try {
+            Files.write(filesSetup.getDataFilePath(), TEST_FILE_DATA_2.getBytes(), READ);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    @Test
+    public void test_write$Path$byte$OpenOption_NPE() throws IOException {
+        try {
+            Files.write(null, TEST_FILE_DATA_2.getBytes(), CREATE_NEW);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.write(filesSetup.getTestPath(), (byte[]) null, CREATE_NEW);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.write(filesSetup.getTestPath(), TEST_FILE_DATA_2.getBytes(), (OpenOption[]) null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_write$Path$Iterable$Charset$OpenOption() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add(TEST_FILE_DATA_2);
+        lines.add(TEST_FILE_DATA);
+        Files.write(filesSetup.getDataFilePath(), lines, StandardCharsets.UTF_16);
+        List<String> readLines = Files.readAllLines(filesSetup.getDataFilePath(),
+                StandardCharsets.UTF_16);
+        assertEquals(readLines, lines);
+    }
+
+    @Test
+    public void test_write$Path$Iterable$Charset$OpenOption_NPE() throws IOException {
+        try {
+            Files.write(null, new ArrayList<>(), StandardCharsets.UTF_16);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.write(filesSetup.getDataFilePath(), null, StandardCharsets.UTF_16);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.write(filesSetup.getDataFilePath(), new ArrayList<>(), (OpenOption[]) null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_write$Path$Iterable$OpenOption() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add(TEST_FILE_DATA_2);
+        lines.add(TEST_FILE_DATA);
+        Files.write(filesSetup.getDataFilePath(), lines);
+        List<String> readLines = Files.readAllLines(filesSetup.getDataFilePath());
+        assertEquals(readLines, lines);
+    }
+
+    @Test
+    public void test_write$Path$Iterable$OpenOption_NPE() throws IOException {
+        try {
+            Files.write(null, new ArrayList<String>());
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.write(filesSetup.getDataFilePath(), (Iterable<CharSequence>) null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    // The ability for Android apps to create hard links was removed in
+    // https://android-review.googlesource.com/144092 (March 2015).
+    // https://b/19953790.
+    @Test
+    public void test_createLink() throws IOException {
+        try {
+            Files.createLink(filesSetup.getTestPath(), filesSetup.getDataFilePath());
+            fail();
+        } catch (AccessDeniedException expected) {}
+    }
+
+    @Test
+    public void test_createTempDirectory$Path$String$FileAttributes() throws IOException {
+        Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+        String tmpDir = "tmpDir";
+        Path tmpDirPath = Files.createTempDirectory(filesSetup.getTestDirPath(), tmpDir, attr);
+        assertTrue(tmpDirPath.getFileName().toString().startsWith(tmpDir));
+        assertEquals(filesSetup.getTestDirPath(), tmpDirPath.getParent());
+        assertTrue(Files.isDirectory(tmpDirPath));
+        assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+        filesSetup.reset();
+        // Test case when prefix is null.
+        tmpDirPath = Files.createTempDirectory(filesSetup.getTestDirPath(), null, attr);
+        assertEquals(filesSetup.getTestDirPath(), tmpDirPath.getParent());
+        assertTrue(Files.isDirectory(tmpDirPath));
+        assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+        try {
+            Files.createTempDirectory(null, tmpDir, attr);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.createTempDirectory(filesSetup.getTestDirPath(), tmpDir, null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_createTempDirectory$String$FileAttributes() throws IOException {
+        Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+        Path tmpDirectoryLocation = Paths.get(System.getProperty("java.io.tmpdir"));
+
+        String tmpDir = "tmpDir";
+        Path tmpDirPath = Files.createTempDirectory(tmpDir, attr);
+        assertTrue(tmpDirPath.getFileName().toString().startsWith(tmpDir));
+        assertEquals(tmpDirectoryLocation, tmpDirPath.getParent());
+        assertTrue(Files.isDirectory(tmpDirPath));
+        assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+        // Test case when prefix is null.
+        filesSetup.reset();
+        tmpDirPath = Files.createTempDirectory(null, attr);
+        assertEquals(tmpDirectoryLocation, tmpDirPath.getParent());
+        assertTrue(Files.isDirectory(tmpDirPath));
+        assertEquals(attr.value(), Files.getAttribute(tmpDirPath, attr.name()));
+
+        try {
+            Files.createTempDirectory(tmpDir, null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_createTempFile$Path$String$String$FileAttributes() throws IOException {
+        Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+        String tmpFilePrefix = "prefix";
+        String tmpFileSuffix = "suffix";
+
+        Path tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix,
+                tmpFileSuffix, attr);
+
+        assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+        assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+        assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent());
+        assertTrue(Files.isRegularFile(tmpFilePath));
+        assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+        // Test case when prefix is null.
+        filesSetup.reset();
+        tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), null,
+                tmpFileSuffix, attr);
+        assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+        assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent());
+        assertTrue(Files.isRegularFile(tmpFilePath));
+        assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+        // Test case when suffix is null.
+        filesSetup.reset();
+        tmpFilePath = Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix,
+                null, attr);
+        assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+        assertEquals(filesSetup.getTestDirPath(), tmpFilePath.getParent());
+        assertTrue(Files.isRegularFile(tmpFilePath));
+        assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+        try {
+            Files.createTempFile(null, tmpFilePrefix, tmpFileSuffix, attr);
+            fail();
+        } catch (NullPointerException expected) {}
+
+        try {
+            Files.createTempFile(filesSetup.getTestDirPath(), tmpFilePrefix, tmpFileSuffix,
+                    null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void test_createTempFile$String$String$FileAttributes() throws IOException {
+        Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
+        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
+
+        Path tmpDirectoryLocation = Paths.get(System.getProperty(
+                "java.io.tmpdir"));
+
+        String tmpFilePrefix = "prefix";
+        String tmpFileSuffix = "suffix";
+        Path tmpFilePath = Files.createTempFile(tmpFilePrefix, tmpFileSuffix, attr);
+        assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+        assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+        assertEquals(tmpDirectoryLocation, tmpFilePath.getParent());
+        assertTrue(Files.isRegularFile(tmpFilePath));
+        assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+        // Test case when prefix is null.
+        filesSetup.reset();
+        tmpFilePath = Files.createTempFile(null, tmpFileSuffix, attr);
+        assertEquals(tmpDirectoryLocation, tmpFilePath.getParent());
+        assertTrue(tmpFilePath.getFileName().toString().endsWith(tmpFileSuffix));
+        assertTrue(Files.isRegularFile(tmpFilePath));
+        assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+        // Test case when suffix is null.
+        filesSetup.reset();
+        tmpFilePath = Files.createTempFile(tmpFilePrefix, null, attr);
+        assertEquals(tmpDirectoryLocation, tmpFilePath.getParent());
+        assertTrue(tmpFilePath.getFileName().toString().startsWith(tmpFilePrefix));
+        assertTrue(Files.isRegularFile(tmpFilePath));
+        assertEquals(attr.value(), Files.getAttribute(tmpFilePath, attr.name()));
+
+        try {
+            Files.createTempFile(tmpFilePrefix, tmpFileSuffix, null);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
     // -- Mock Class --
 
     private static class TestFileVisitor implements FileVisitor<Path> {
diff --git a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
index 66201d9..70e717d 100644
--- a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
+++ b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
@@ -45,6 +45,13 @@
 
     final static String TEST_FILE_DATA_2 = "test";
 
+    /** 
+     *  Data that includes characters code above the US-ASCII range and will be more obviously
+     *  corrupted if encoded / decoded incorrectly than
+     *  {@link #TEST_FILE_DATA} / {@link #TEST_FILE_DATA_2}.
+     */
+    final static String UTF_16_DATA = "परीक्षण";
+
     private String testDir;
 
     private Path dataFilePath;
diff --git a/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java b/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java
index 63f3fd3..c53f531 100644
--- a/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/WatchServiceTest.java
@@ -113,17 +113,34 @@
 
         // emit EVENT_CREATE
         Files.createFile(file);
+        assertWatchServiceEvent(watchService, directoryKey1,
+            Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), true);
+        assertNull(watchService.poll());
+      
         // emit EVENT_MODIFY
         Files.write(file, "hello1".getBytes());
+        assertWatchServiceEvent(watchService, directoryKey1,
+            Arrays.asList(new WatchEventResult(ENTRY_MODIFY)), true);
+
+        // http:///b/35346596
+        // Sometimes we receive a second, latent EVENT_MODIFY that happens shortly
+        // after the first one. This will intercept it and make sure it won't
+        // mess with ENTRY_DELETE later.
+        Thread.sleep(500);
+        WatchKey doubleModifyKey = watchService.poll();
+        if (doubleModifyKey != null) {
+            List<WatchEvent<?>> event = doubleModifyKey.pollEvents();
+            assertEquals(ENTRY_MODIFY, event.get(0).kind());
+            doubleModifyKey.reset();
+        }
+        assertNull(watchService.poll());      
+
         // emit EVENT_DELETE
         Files.delete(file);
-
-        // Don't assert count of ENTRY_MODIFY, it's very flaky, sometime it triggers
-        // two events, sometime one....
         assertWatchServiceEvent(watchService, directoryKey1,
-                Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
-                              new WatchEventResult(ENTRY_MODIFY),
-                              new WatchEventResult(ENTRY_DELETE, 1)), true);
+            Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true);
+
+        // Assert no more events
         assertNull(watchService.poll());
         watchService.close();
     }
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index d9d0d31..ffba98a 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -35,8 +35,10 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -44,6 +46,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -84,6 +87,20 @@
             Set<Provider.Service> services = provider.getServices();
             assertNotNull(services);
             assertFalse(services.isEmpty());
+            if (LOG_DEBUG) {
+                Set<Provider.Service> originalServices = services;
+                services = new TreeSet<Provider.Service>(
+                        new Comparator<Provider.Service>() {
+                            public int compare(Provider.Service a, Provider.Service b) {
+                                int typeCompare = a.getType().compareTo(b.getType());
+                                if (typeCompare != 0) {
+                                    return typeCompare;
+                                }
+                                return a.getAlgorithm().compareTo(b.getAlgorithm());
+                            }
+                        });
+                services.addAll(originalServices);
+            }
 
             for (Provider.Service service : services) {
                 String type = service.getType();
@@ -148,10 +165,28 @@
 
         // assert that we don't have any extra in the implementation
         Collections.sort(extra); // sort so that its grouped by type
-        assertEquals("Extra algorithms", Collections.EMPTY_LIST, extra);
+        assertEquals("Algorithms are provided but not present in StandardNames",
+                Collections.EMPTY_LIST, extra);
 
+        if (remainingExpected.containsKey("Cipher")) {
+            // For any remaining ciphers, they may be aliases for other ciphers or otherwise
+            // don't show up as a service but can still be instantiated.
+            for (Iterator<String> cipherIt = remainingExpected.get("Cipher").iterator();
+                    cipherIt.hasNext(); ) {
+                String missingCipher = cipherIt.next();
+                try {
+                    Cipher.getInstance(missingCipher);
+                    cipherIt.remove();
+                } catch (NoSuchAlgorithmException|NoSuchPaddingException e) {
+                }
+            }
+            if (remainingExpected.get("Cipher").isEmpty()) {
+                remainingExpected.remove("Cipher");
+            }
+        }
         // assert that we don't have any missing in the implementation
-        assertEquals("Missing algorithms", Collections.EMPTY_MAP, remainingExpected);
+        assertEquals("Algorithms are present in StandardNames but not provided",
+                Collections.EMPTY_MAP, remainingExpected);
 
         // assert that we don't have any missing classes
         Collections.sort(missing); // sort it for readability
diff --git a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
index b95f9fe..20fa986 100644
--- a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
@@ -557,4 +557,30 @@
         dateFormat.setTimeZone(UTC);
         assertEquals("UTC", dateFormat.format(new Date(0)));
     }
+
+    // http://b/35134326
+    public void testTimeZoneParsingErrorIndex() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy z", Locale.ENGLISH);
+
+        checkTimeZoneParsingErrorIndex(dateFormat);
+    }
+
+    // http://b/35134326
+    public void testTimeZoneParsingErrorIndexWithZoneStrings() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy z", Locale.ENGLISH);
+        // Force legacy code path by using zone strings.
+        DateFormatSymbols dfs = dateFormat.getDateFormatSymbols();
+        dfs.setZoneStrings(dfs.getZoneStrings());
+        dateFormat.setDateFormatSymbols(dfs);
+
+        checkTimeZoneParsingErrorIndex(dateFormat);
+    }
+
+    private void checkTimeZoneParsingErrorIndex(SimpleDateFormat dateFormat) {
+        ParsePosition pos = new ParsePosition(0);
+        Date parsed;
+        parsed = dateFormat.parse("2000 foobar", pos);
+        assertNull(parsed);
+        assertEquals("Wrong error index", 5, pos.getErrorIndex());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java b/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java
new file mode 100644
index 0000000..3b7255f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/DateTimeExceptionTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link DateTimeException}.
+ */
+public class DateTimeExceptionTest {
+
+    @Test
+    public void test_constructor_message() {
+        DateTimeException ex = new DateTimeException("message");
+        assertEquals("message", ex.getMessage());
+        assertNull(ex.getCause());
+
+    }
+
+    @Test
+    public void test_constructor_message_cause() {
+        Throwable cause = new Exception();
+        DateTimeException ex = new DateTimeException("message", cause);
+        assertEquals("message", ex.getMessage());
+        assertSame(cause, ex.getCause());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/DurationTest.java b/luni/src/test/java/libcore/java/time/DurationTest.java
new file mode 100644
index 0000000..574a1d5
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/DurationTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.chrono.MinguoChronology;
+import java.time.temporal.Temporal;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link Duration}.
+ *
+ * @see tck.java.time.TCKDuration
+ * @see test.java.time.TestDuration
+ */
+public class DurationTest {
+
+    // Hardcoded maximum representable duration.
+    public static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999);
+
+    @Test
+    public void test_addTo() {
+        assertSame(Instant.EPOCH, Duration.ZERO.addTo(Instant.EPOCH));
+
+        // These tests are a little tautological, but since Duration.between is well-tested,
+        // they are still valuable.
+        assertEquals(Instant.MAX,
+                Duration.between(Instant.EPOCH, Instant.MAX).addTo(Instant.EPOCH));
+        assertEquals(Instant.MAX,
+                Duration.between(Instant.MIN, Instant.MAX).addTo(Instant.MIN));
+        assertEquals(Instant.EPOCH,
+                Duration.between(Instant.MIN, Instant.EPOCH).addTo(Instant.MIN));
+    }
+
+    @Test
+    public void test_subtractFrom() {
+        assertSame(Instant.EPOCH, Duration.ZERO.subtractFrom(Instant.EPOCH));
+        assertEquals(Instant.MIN,
+                Duration.between(Instant.MIN, Instant.EPOCH).subtractFrom(Instant.EPOCH));
+        assertEquals(Instant.MIN,
+                Duration.between(Instant.MIN, Instant.MAX).subtractFrom(Instant.MAX));
+        assertEquals(Instant.EPOCH,
+                Duration.between(Instant.EPOCH, Instant.MAX).subtractFrom(Instant.MAX));
+    }
+
+    @Test
+    public void test_addTo_exceeds() {
+        Object[][] breakingValues = new Object[][] {
+                { Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MAX).plusNanos(1) },
+                // Adding a negative duration is the same as subtracting the negated value.
+                { Instant.EPOCH, Duration.between(Instant.EPOCH, Instant.MIN).minusNanos(1) },
+                { Instant.EPOCH, Duration.between(Instant.MIN, Instant.MAX) },
+                { Instant.EPOCH, MAX_DURATION },
+                { Instant.MIN, MAX_DURATION },
+                { Instant.MAX, Duration.ofNanos(1) },
+                { LocalDateTime.MAX, Duration.ofNanos(1) },
+                { LocalDateTime.now(), MAX_DURATION },
+                { ZonedDateTime.of(LocalDateTime.MAX, ZoneOffset.UTC ), Duration.ofNanos(1) },
+        };
+
+        for (Object[] values : breakingValues) {
+            Temporal temporal = (Temporal) values[0];
+            Duration duration = (Duration) values[1];
+
+            try {
+                duration.addTo(temporal);
+                fail(" Should have failed to add " + duration + " to " + temporal);
+            } catch (DateTimeException expected) {
+            }
+        }
+    }
+
+    @Test
+    public void test_subtractFrom_exceeds() {
+        Object[][] breakingValues = new Object[][] {
+                { Instant.EPOCH, Duration.between(Instant.MIN, Instant.EPOCH).plusNanos(1) },
+                // Subtracting a negative Duration is the same as adding the negated value.
+                { Instant.EPOCH, Duration.between(Instant.MAX, Instant.EPOCH).minusNanos(1) },
+                { Instant.EPOCH, Duration.between(Instant.MIN, Instant.MAX) },
+                { Instant.EPOCH, MAX_DURATION },
+                { Instant.MAX, MAX_DURATION },
+                { Instant.MIN, Duration.ofNanos(1) },
+                { LocalDateTime.MIN, Duration.ofNanos(1) },
+                { LocalDateTime.now(), MAX_DURATION },
+                { LocalDateTime.MAX, MAX_DURATION },
+                { ZonedDateTime.of(LocalDateTime.MIN, ZoneOffset.UTC ), Duration.ofNanos(1) },
+        };
+
+        for (Object[] values : breakingValues) {
+            Temporal temporal = (Temporal) values[0];
+            Duration duration = (Duration) values[1];
+
+            try {
+                duration.subtractFrom(temporal);
+                fail("Should have failed to subtract " + duration + " from " + temporal);
+            } catch (DateTimeException expected) {
+            }
+        }
+    }
+
+    @Test
+    public void test_addTo_subtractFrom_unsupported() {
+        // These Temporal objects don't supports seconds/nanos.
+        // The actual values of those Temporals don't matter, only their type is checked.
+        Temporal[] unsupportedTemporals = new Temporal[] {
+                Year.now(),
+                YearMonth.now(),
+                LocalDate.now(),
+                // An arbitrary ChronoLocalDateImpl as a representative example.
+                MinguoChronology.INSTANCE.dateNow(),
+        };
+
+        Duration second = Duration.ofSeconds(1);
+        for (Temporal temporal : unsupportedTemporals) {
+            // Adding/subtracting zero should not fail.
+            assertSame(temporal, Duration.ZERO.addTo(temporal));
+            assertSame(temporal, Duration.ZERO.subtractFrom(temporal));
+
+            try {
+                second.addTo(temporal);
+                fail("Should not be able to add a duration  to " + temporal);
+            } catch (UnsupportedTemporalTypeException expected) {
+            }
+            try {
+                second.subtractFrom(temporal);
+                fail("Should not be able to subtract a duration  from " + temporal);
+            } catch (UnsupportedTemporalTypeException expected) {
+            }
+        }
+
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/InstantTest.java b/luni/src/test/java/libcore/java/time/InstantTest.java
new file mode 100644
index 0000000..ba31264
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/InstantTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link Instant}.
+ *
+ * @see tck.java.time.TCKInstant
+ * @see test.java.time.TestInstant
+ */
+public class InstantTest {
+
+    @Test
+    public void test_isSupported_TemporalUnit() {
+        assertEquals(false, Instant.EPOCH.isSupported((TemporalUnit) null));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.NANOS));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MICROS));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MILLIS));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.SECONDS));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.MINUTES));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.HOURS));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.HALF_DAYS));
+        assertEquals(true, Instant.EPOCH.isSupported(ChronoUnit.DAYS));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.WEEKS));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.MONTHS));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.YEARS));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.DECADES));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.CENTURIES));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.MILLENNIA));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.ERAS));
+        assertEquals(false, Instant.EPOCH.isSupported(ChronoUnit.FOREVER));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/LocalDateTest.java b/luni/src/test/java/libcore/java/time/LocalDateTest.java
new file mode 100644
index 0000000..f0c8db2
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/LocalDateTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.chrono.IsoChronology;
+
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link LocalDate}.
+ *
+ * @see tck.java.time.TCKLocalDate
+ * @see test.java.time.TestLocalDate
+ */
+public class LocalDateTest {
+
+    @Test
+    public void test_getChronology() {
+        // LocalDate always uses the IsoChronology.
+        assertSame(IsoChronology.INSTANCE, LocalDate.MIN.getChronology());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java
new file mode 100644
index 0000000..09a258a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link OffsetDateTime}.
+ *
+ * @see tck.java.time.TCKOffsetDateTime
+ * @see test.java.time.TestOffsetDateTime
+ * @see test.java.time.TestOffsetDateTime_instants
+ */
+public class OffsetDateTimeTest {
+
+    private static final OffsetDateTime ODT =
+            OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 6, ZoneOffset.UTC);
+    //                        2000-01-02T03:04:05.000000006 UTC
+
+    @Test
+    public void test_plus() {
+        // Most of the logic is in LocalDateTime, to which OffsetDateTime#plus() delegates, verify
+        // only some simple cases here. In-depth tests for LocalDateTime#plus() can be found in
+        // TCKLocalDateTime.
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 4, 4, 5, 6, ZoneOffset.UTC),
+                ODT.plus(1, ChronoUnit.HOURS));
+        assertEquals(OffsetDateTime.of(2000, 1, 3, 2, 4, 5, 6, ZoneOffset.UTC),
+                ODT.plus(23, ChronoUnit.HOURS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 5, 5, 6, ZoneOffset.UTC),
+                ODT.plus(1, ChronoUnit.MINUTES));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 5, 5, 6, ZoneOffset.UTC),
+                ODT.plus(60, ChronoUnit.SECONDS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 1_000_006, ZoneOffset.UTC),
+                ODT.plus(1, ChronoUnit.MILLIS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 7, ZoneOffset.UTC),
+                ODT.plus(1, ChronoUnit.NANOS));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/OffsetTimeTest.java b/luni/src/test/java/libcore/java/time/OffsetTimeTest.java
new file mode 100644
index 0000000..880343f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/OffsetTimeTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.OffsetTime;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.EnumSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link OffsetTime}.
+ *
+ * @see tck.java.time.TCKOffsetTime
+ * @see test.java.time.TestOffsetTime
+ */
+public class OffsetTimeTest {
+
+    private static final OffsetTime NOON_UTC = OffsetTime
+            .of(/* hour */ 12, /* minute */ 0, /* second */ 0, /* nano */ 0, ZoneOffset.UTC);
+
+    @Test
+    public void test_plus() {
+        // Most of the logic is in LocalTime, to which OffsetTime#plus() delegates, verify only some
+        // simple cases here. In-depth tests for LocalTime#plus() can be found in TCKLocalTime.
+        assertEquals(OffsetTime.of(13, 0, 0, 0, ZoneOffset.UTC),
+                NOON_UTC.plus(1, ChronoUnit.HOURS));
+        assertEquals(OffsetTime.of(11, 0, 0, 0, ZoneOffset.UTC),
+                NOON_UTC.plus(23, ChronoUnit.HOURS));
+        assertEquals(OffsetTime.of(12, 1, 0, 0, ZoneOffset.UTC),
+                NOON_UTC.plus(1, ChronoUnit.MINUTES));
+        assertEquals(OffsetTime.of(12, 1, 0, 0, ZoneOffset.UTC),
+                NOON_UTC.plus(60, ChronoUnit.SECONDS));
+        assertEquals(OffsetTime.of(12, 0, 0, 1_000_000, ZoneOffset.UTC),
+                NOON_UTC.plus(1, ChronoUnit.MILLIS));
+        assertEquals(OffsetTime.of(12, 0, 0, 1, ZoneOffset.UTC),
+                NOON_UTC.plus(1, ChronoUnit.NANOS));
+    }
+
+    @Test
+    public void test_plus_noop() {
+        assertPlusIsNoop(0, ChronoUnit.MINUTES);
+        assertPlusIsNoop(2, ChronoUnit.HALF_DAYS);
+        assertPlusIsNoop(24, ChronoUnit.HOURS);
+        assertPlusIsNoop(24 * 60, ChronoUnit.MINUTES);
+        assertPlusIsNoop(24 * 60 * 60, ChronoUnit.SECONDS);
+        assertPlusIsNoop(24 * 60 * 60 * 1_000, ChronoUnit.MILLIS);
+        assertPlusIsNoop(24 * 60 * 60 * 1_000_000_000L, ChronoUnit.NANOS);
+    }
+
+    private static void assertPlusIsNoop(long amount, TemporalUnit unit) {
+        assertSame(NOON_UTC, NOON_UTC.plus(amount, unit));
+    }
+
+    @Test
+    public void test_plus_minus_invalidUnits() {
+        for (ChronoUnit unit : EnumSet.range(ChronoUnit.DAYS, ChronoUnit.FOREVER)) {
+            try {
+                NOON_UTC.plus(1, unit);
+                fail("Adding 1 " + unit + " should have failed.");
+            } catch (UnsupportedTemporalTypeException expected) {
+            }
+            try {
+                NOON_UTC.minus(1, unit);
+                fail("Subtracting 1 " + unit + " should have failed.");
+            } catch (UnsupportedTemporalTypeException expected) {
+            }
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/PeriodTest.java b/luni/src/test/java/libcore/java/time/PeriodTest.java
new file mode 100644
index 0000000..5d312a9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/PeriodTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.Period;
+import java.time.chrono.IsoChronology;
+
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link Period}.
+ *
+ * @see tck.java.time.TCKPeriod
+ * @see test.java.time.TestPeriod
+ */
+public class PeriodTest {
+    @Test
+    public void test_getChronology() {
+        // Period always uses the IsoChronology.
+        assertSame(IsoChronology.INSTANCE, Period.ZERO.getChronology());
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/time/YearMonthTest.java b/luni/src/test/java/libcore/java/time/YearMonthTest.java
new file mode 100644
index 0000000..c4a3e79
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/YearMonthTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.Month;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.chrono.IsoEra;
+import java.time.temporal.ChronoField;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link YearMonth}.
+ *
+ * @see tck.java.time.TCKYearMonth
+ * @see test.java.time.TestYearMonth
+ */
+public class YearMonthTest {
+
+    @Test
+    public void test_with_TemporalField_long() {
+        YearMonth ym = YearMonth.of(2000, Month.JANUARY);
+        // -1999 is actually 2000 BCE (and 0 is 1 BCE).
+        YearMonth bceYm = YearMonth.of(-1999, Month.JANUARY);
+
+        assertEquals(YearMonth.of(1000, Month.JANUARY), ym.with(ChronoField.YEAR, 1000));
+        assertEquals(YearMonth.of(-1, Month.JANUARY), ym.with(ChronoField.YEAR, -1));
+        assertEquals(YearMonth.of(2000, Month.FEBRUARY), ym.with(ChronoField.MONTH_OF_YEAR, 2));
+        assertEquals(YearMonth.of(-1999, Month.DECEMBER),
+                bceYm.with(ChronoField.MONTH_OF_YEAR, 12));
+        assertSame(ym, ym.with(ChronoField.ERA, IsoEra.CE.getValue()));
+        assertSame(bceYm, bceYm.with(ChronoField.ERA, IsoEra.BCE.getValue()));
+
+        assertEquals(bceYm, ym.with(ChronoField.ERA, IsoEra.BCE.getValue()));
+        assertEquals(ym, bceYm.with(ChronoField.ERA, IsoEra.CE.getValue()));
+        assertEquals(YearMonth.of(1, Month.JANUARY), ym.with(ChronoField.YEAR_OF_ERA, 1));
+        // Proleptic year 0 is 1 BCE.
+        assertEquals(YearMonth.of(0, Month.JANUARY), bceYm.with(ChronoField.YEAR_OF_ERA, 1));
+        assertEquals(YearMonth.of(0, Month.JANUARY), ym.with(ChronoField.PROLEPTIC_MONTH, 0));
+        assertEquals(YearMonth.of(Year.MAX_VALUE, Month.DECEMBER), ym.with(ChronoField.PROLEPTIC_MONTH, Year.MAX_VALUE * 12L + 11));
+        assertEquals(YearMonth.of(Year.MIN_VALUE, Month.JANUARY), ym.with(ChronoField.PROLEPTIC_MONTH, Year.MIN_VALUE * 12L));
+    }
+
+    @Test
+    public void test_with_TemporalField_long_invalidValue() {
+        Object[][] invalidValues = new Object[][] {
+                { ChronoField.YEAR_OF_ERA, 0 },
+                { ChronoField.YEAR_OF_ERA, Year.MAX_VALUE + 1 },
+                { ChronoField.YEAR, Year.MIN_VALUE - 1 },
+                { ChronoField.YEAR, Year.MAX_VALUE + 1 },
+                { ChronoField.ERA, -1 },
+                { ChronoField.ERA, 2 },
+                { ChronoField.MONTH_OF_YEAR, -1 },
+                { ChronoField.MONTH_OF_YEAR, 0 },
+                { ChronoField.MONTH_OF_YEAR, 13 },
+                { ChronoField.PROLEPTIC_MONTH, Year.MAX_VALUE * 12L + 12 },
+                { ChronoField.PROLEPTIC_MONTH, Year.MIN_VALUE * 12L - 1 },
+        };
+
+        YearMonth ym = YearMonth.of(2000, Month.JANUARY);
+        for (Object[] values : invalidValues) {
+            ChronoField field = (ChronoField) values[0];
+            long value = ((Number) values[1]).longValue();
+            try {
+                ym.with(field, value);
+                fail("ym.with(" + field + ", " + value + ") should have failed.");
+            } catch (DateTimeException expected) {
+            }
+        }
+
+    }
+
+    @Test
+    public void test_with_TemporalField_long_invalidField() {
+        ChronoField[] invalidFields = new ChronoField[] {
+                ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH,
+                ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR,
+                ChronoField.ALIGNED_WEEK_OF_MONTH,
+                ChronoField.ALIGNED_WEEK_OF_YEAR,
+                ChronoField.AMPM_OF_DAY,
+                ChronoField.CLOCK_HOUR_OF_AMPM,
+                ChronoField.CLOCK_HOUR_OF_DAY,
+                ChronoField.DAY_OF_MONTH,
+                ChronoField.DAY_OF_WEEK,
+                ChronoField.DAY_OF_YEAR,
+                ChronoField.EPOCH_DAY,
+                ChronoField.HOUR_OF_AMPM,
+                ChronoField.HOUR_OF_DAY,
+                ChronoField.INSTANT_SECONDS,
+                ChronoField.MICRO_OF_DAY,
+                ChronoField.MICRO_OF_SECOND,
+                ChronoField.MILLI_OF_DAY,
+                ChronoField.MILLI_OF_SECOND,
+                ChronoField.MINUTE_OF_DAY,
+                ChronoField.MINUTE_OF_HOUR,
+                ChronoField.NANO_OF_DAY,
+                ChronoField.NANO_OF_SECOND,
+                ChronoField.OFFSET_SECONDS,
+                ChronoField.SECOND_OF_DAY,
+                ChronoField.SECOND_OF_MINUTE,
+        };
+
+        YearMonth ym = YearMonth.of(2000, Month.JANUARY);
+        for (ChronoField invalidField : invalidFields) {
+            // Get a valid value to ensure we fail to due invalid field, not due to invalid value.
+            long value = invalidField.range().getMinimum();
+            try {
+                ym.with(invalidField, value);
+                fail("TemporalField.with() should not accept " + invalidField);
+            } catch (UnsupportedTemporalTypeException expected) {
+            }
+        }
+
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/YearTest.java b/luni/src/test/java/libcore/java/time/YearTest.java
new file mode 100644
index 0000000..3454391
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/YearTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.Year;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Additional tests for {@link Year}.
+ *
+ * @see tck.java.time.TCKYear
+ * @see test.java.time.TestYear
+ */
+public class YearTest {
+    @Test
+    public void test_isLeap() {
+        // More extensive tests for Year.isLeap() (which delegates to this static method) can be
+        // found in tck.java.time.TCKYear.test_isLeap()
+        assertFalse(Year.isLeap(1900));
+        assertFalse(Year.isLeap(1999));
+        assertTrue(Year.isLeap(2000));
+        assertFalse(Year.isLeap(2001));
+        assertFalse(Year.isLeap(2002));
+        assertFalse(Year.isLeap(2003));
+        assertTrue(Year.isLeap(2004));
+        assertFalse(Year.isLeap(2005));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java b/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java
new file mode 100644
index 0000000..1a37257
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/ZoneOffsetTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoField;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link ZoneOffset}.
+ *
+ * @see tck.java.time.TCKZoneOffset
+ * @see test.java.time.TestZoneOffset
+ */
+public class ZoneOffsetTest {
+
+    private static final ZoneOffset OFFSET_P1 = ZoneOffset.ofHours(1);
+
+    @Test
+    public void test_isSupported() {
+        for (ChronoField field : ChronoField.values()) {
+            // Only OFFSET_SECONDS is supported.
+            assertEquals("isSupported(" + field + ")",
+                    field == ChronoField.OFFSET_SECONDS, OFFSET_P1.isSupported(field));
+        }
+    }
+
+    @Test
+    public void test_range() {
+        assertEquals(ChronoField.OFFSET_SECONDS.range(),
+                OFFSET_P1.range(ChronoField.OFFSET_SECONDS));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_range_null() {
+        OFFSET_P1.range(null);
+    }
+
+    @Test
+    public void test_range_unsupported() {
+        for (ChronoField field : ChronoField.values()) {
+            // Only OFFSET_SECONDS is supported.
+            if (field == ChronoField.OFFSET_SECONDS) {
+                continue;
+            }
+            try {
+                OFFSET_P1.range(field);
+                fail("ZoneOffset.range(" + field + ") should have failed.");
+            } catch (UnsupportedTemporalTypeException expected) {
+            }
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java
new file mode 100644
index 0000000..09422dd
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 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.time;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Additional tets for {@link ZonedDateTime}.
+ *
+ * @see tck.java.time.TCKZonedDateTime
+ * @see test.java.time.TestZonedDateTime
+ */
+public class ZonedDateTimeTest {
+
+    // Europe/Vienna is UTC+2 during summer, UTC+1 during winter.
+    private static final ZoneId ZONE_VIENNA = ZoneId.of("Europe/Vienna");
+
+    // UTC+1, the offset during winter time.
+    private static final ZoneOffset OFFSET_P1 = ZoneOffset.ofHours(1);
+
+    // UTC+2, the offset during summer time.
+    private static final ZoneOffset OFFSET_P2 = ZoneOffset.ofHours(2);
+
+    // LocalDateTime during winter time (OFFSET_P1 in ZONE_VIENNA).
+    private static final LocalDateTime LDT_P1 = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0);
+
+    // LocalDateTime during summer time (OFFSET_P2 in ZONE_VIENNA).
+    private static final LocalDateTime LDT_P2 = LocalDateTime.of(2000, Month.JUNE, 1, 0, 0);
+
+    // LocalDateTime that is in a gap that occurs at the switch from winter time to summer time.
+    // This is not a valid local time in ZONE_VIENNA.
+    private static final LocalDateTime LDT_IN_GAP = LocalDateTime.of(2000, Month.MARCH, 26, 2, 30);
+
+    // LocalDateTime that is in an overlap that occurs at the switch from summer time to winter
+    // time. This LDT actually occurs twice in ZONE_VIENNA.
+    private static final LocalDateTime LDT_IN_OVERLAP =
+            LocalDateTime.of(2000, Month.OCTOBER, 29, 2, 30);
+
+    @Test
+    public void test_ofInstant() {
+        // ofInstant behaves as if it calculated an Instant from the LocalDateTime/ZoneOffset
+        // and then calling ofInstant(Instant, ZoneId). That's why "invalid" zone offsets are
+        // tolerated and basically just change how the LocalDateTime is interpreted.
+
+        // checkOfInstant(localDateTime, offset, zone, expectedDateTime, expectedOffset)
+
+        // Correct offset in summer.
+        checkOfInstant(LDT_P1, OFFSET_P1, ZONE_VIENNA, LDT_P1, OFFSET_P1);
+        // Correct offset in winter.
+        checkOfInstant(LDT_P2, OFFSET_P2, ZONE_VIENNA, LDT_P2, OFFSET_P2);
+        // "Wrong" offset in winter.
+        checkOfInstant(LDT_P1, OFFSET_P2, ZONE_VIENNA, LDT_P1.minusDays(1).withHour(23), OFFSET_P1);
+        // "Wrong" offset in summer.
+        checkOfInstant(LDT_P2, OFFSET_P1, ZONE_VIENNA, LDT_P2.withHour(1), OFFSET_P2);
+
+        // Very wrong offset in winter.
+        checkOfInstant(LDT_P1, ZoneOffset.ofHours(-10), ZONE_VIENNA, LDT_P1.withHour(11),
+                OFFSET_P1);
+
+        // Neither of those combinations exist, so they are interpreted as either before or after
+        // the gap, depending on the offset.
+        checkOfInstant(LDT_IN_GAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_GAP.plusHours(1), OFFSET_P2);
+        checkOfInstant(LDT_IN_GAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_GAP.minusHours(1), OFFSET_P1);
+
+        // Both combinations exist and are valid, so they produce exactly the input.
+        checkOfInstant(LDT_IN_OVERLAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P1);
+        checkOfInstant(LDT_IN_OVERLAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P2);
+    }
+
+    /**
+     * Assert that calling {@link ZonedDateTime#ofInstant(LocalDateTime, ZoneOffset, ZoneId)} with
+     * the first three parameters produces a sane result with the localDateTime and offset equal to
+     * the last two.
+     */
+    private static void checkOfInstant(LocalDateTime localDateTime, ZoneOffset offset,
+            ZoneId zone, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) {
+        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(localDateTime, offset, zone);
+        String message = String.format(" for ofInstant(%s, %s, %s) = %s, ",
+                localDateTime, offset, zone, zonedDateTime);
+        // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(),
+        // specifically when offset is not a valid offset for zone at localDateTime (or ever).
+        assertEquals("zone" + message, zone, zonedDateTime.getZone());
+
+        assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset());
+        assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime());
+        if (offset.equals(expectedOffset)) {
+            // When we get same offset, the localDateTime must be the same as the input. This
+            // assert basically just verifies that the test is written correctly.
+            assertEquals("expected localDateTime" + message,
+                    expectedDateTime, zonedDateTime.toLocalDateTime());
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_ofInstant_localDateTime_null() {
+        ZonedDateTime.ofInstant(null, OFFSET_P1, ZONE_VIENNA);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_ofInstant_offset_null() {
+        ZonedDateTime.ofInstant(LDT_P1, null, ZONE_VIENNA);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_ofInstant_zone_null() {
+        ZonedDateTime.ofInstant(LDT_P1, OFFSET_P1, null);
+    }
+
+    @Test
+    public void test_ofLocal() {
+        // checkOfLocal(localDateTime, zone, preferredOffset, expectedDateTime, expectedOffset)
+
+        // Correct offset in summer.
+        checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P1, LDT_P1, OFFSET_P1);
+        // Correct offset in winter.
+        checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P2, LDT_P2, OFFSET_P2);
+        // "Wrong" offset in winter.
+        checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P2, LDT_P1, OFFSET_P1);
+        // "Wrong" offset in summer.
+        checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P1, LDT_P2, OFFSET_P2);
+        // Very wrong offset in winter.
+        checkOfLocal(LDT_P1, ZONE_VIENNA, ZoneOffset.ofHours(-10), LDT_P1, OFFSET_P1);
+
+        // Neither of those combinations exist, so they are interpreted as after the gap.
+        checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_GAP.plusHours(1), OFFSET_P2);
+        checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_GAP.plusHours(1), OFFSET_P2);
+
+        // Both combinations exist and are valid, so they produce exactly the input.
+        checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_OVERLAP, OFFSET_P1);
+        checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_OVERLAP, OFFSET_P2);
+
+        // Passing in null for preferredOffset will be biased to the offset before the transition.
+        checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, /* preferredOffset */ null,
+                LDT_IN_OVERLAP, OFFSET_P2);
+        // Passing in an invalid offset will be biased to the offset before the transition.
+        checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, ZoneOffset.ofHours(10),
+                LDT_IN_OVERLAP, OFFSET_P2);
+    }
+
+    /**
+     * Assert that calling {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)} with
+     * the first three parameters produces a sane result with the localDateTime, and offset equal
+     * to the last two.
+     */
+    private static void checkOfLocal(LocalDateTime localDateTime, ZoneId zone,
+            ZoneOffset preferredOffset, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) {
+        ZonedDateTime zonedDateTime = ZonedDateTime.ofLocal(localDateTime, zone, preferredOffset);
+        String message = String.format(" for ofLocal(%s, %s, %s) = %s, ",
+                localDateTime, zone, preferredOffset, zonedDateTime);
+        // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(),
+        // specifically when offset is not a valid offset for zone at localDateTime (or ever).
+        assertEquals("zone" + message, zone, zonedDateTime.getZone());
+        assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset());
+        assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_ofLocal_localDateTime_null() {
+        ZonedDateTime.ofLocal(null, ZONE_VIENNA, OFFSET_P1);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_ofLocal_zone_null() {
+        ZonedDateTime.ofLocal(LDT_P1, null, OFFSET_P1);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java
new file mode 100644
index 0000000..37fdd7c
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.chrono.AbstractChronology;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.time.chrono.Era;
+import java.time.chrono.IsoChronology;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.ValueRange;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link Chronology}.
+ *
+ * @see tck.java.time.chrono.TCKChronology
+ */
+public class ChronologyTest {
+
+    @Test
+    public void test_compareTo() {
+        Set<Chronology> chronologies = new LinkedHashSet<>(Chronology.getAvailableChronologies());
+        chronologies.add(new DummyChronology("aaa", "z aaa"));
+        chronologies.add(new DummyChronology("zzz", "a zzz"));
+
+        // Check for comparison of each chronology with each other (including itself).
+        for (Chronology c1 : chronologies) {
+            for (Chronology c2 : chronologies) {
+                assertComparesAccordingToId(c1, c2);
+            }
+        }
+    }
+
+    private static void assertComparesAccordingToId(Chronology c1, Chronology c2) {
+        int chronologyResult = c1.compareTo(c2);
+        int idResult = c1.getId().compareTo(c2.getId());
+        // note that this message is not strictly true: if two chronologies with the same id but
+        // different parameters exist, then they should return non-zero for compareTo(). That is not
+        // possible with any of the chronologies we currently ship (as of early 2017), though.
+        assertEquals("compareTo() must match getId().compareTo()",
+                (int) Math.signum(chronologyResult), (int) Math.signum(idResult));
+        assertEquals(c1 + " and " + c2 + " compare as equal.",
+                chronologyResult == 0, c1.equals(c2));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_compareTo_null() {
+        IsoChronology.INSTANCE.compareTo(null);
+    }
+
+    /** Dummy chronology that supports only returning an id and a type. */
+    private static class DummyChronology extends AbstractChronology {
+
+        private final String id;
+
+        private final String type;
+
+        public DummyChronology(String id, String type) {
+            this.id = id;
+            this.type = type;
+        }
+
+
+        @Override
+        public String getId() {
+            return id;
+        }
+
+        @Override
+        public String getCalendarType() {
+            return type;
+        }
+
+        @Override
+        public ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoLocalDate dateEpochDay(long epochDay) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoLocalDate date(TemporalAccessor temporal) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isLeapYear(long prolepticYear) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int prolepticYear(Era era, int yearOfEra) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Era eraOf(int eraValue) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public List<Era> eras() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ValueRange range(ChronoField field) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java
new file mode 100644
index 0000000..3a33e5b
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.chrono.HijrahChronology;
+import java.time.chrono.HijrahDate;
+import java.time.chrono.HijrahEra;
+import java.time.temporal.ChronoField;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link HijrahDate}.
+ *
+ * @see tck.java.time.chrono.TCKHijrahChronology
+ */
+public class HijrahChronologyTest {
+    @Test
+    public void test_HijrahDate_getEra() {
+        // HijrahChronology has only one valid era.
+        assertEquals(HijrahEra.AH, HijrahDate.of(1300, 1, 1).getEra());
+    }
+
+    @Test
+    public void test_HijrahDate_getLong() {
+        // 1300 is the first year in the HijrahChronology in the umalqura configuration.
+        HijrahDate date = HijrahDate.of(1300, 2, 5);
+        assertEquals(1300, date.getLong(ChronoField.YEAR_OF_ERA));
+        assertEquals(1300, date.getLong(ChronoField.YEAR));
+        assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR));
+        // Proleptic month starts with 0 for the first month of the proleptic year 0.
+        assertEquals(1300 * 12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH));
+        assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH));
+        // first month of the year 1300 has 30 days.
+        assertEquals(30 + 5, date.getLong(ChronoField.DAY_OF_YEAR));
+        assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY));
+    }
+
+    @Test
+    public void test_HijrahDate_withVariant_same() {
+        // There is currently no way of creating an alternative HijrahChronology, so only this
+        // case and the null case are tested.
+        HijrahDate date1 = HijrahDate.now();
+        HijrahDate date2 = date1.withVariant(HijrahChronology.INSTANCE);
+        assertSame(date1, date2);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_HijrahDate_withVariant_null() {
+        HijrahDate.now().withVariant(null);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java
new file mode 100644
index 0000000..2036f44
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/IsoChronologyTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.Year;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.chrono.IsoChronology;
+import java.time.chrono.IsoEra;
+import java.time.temporal.ChronoField;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link IsoChronology}.
+ *
+ * @see tck.java.time.chrono.TCKIsoChronology
+ */
+public class IsoChronologyTest {
+
+    @Test
+    public void test_dateYear() {
+        int[][] allValues = new int[][] {
+                // proleptic Year, dayOfYear, expectedYear, expectedMonth, expectedDayOfMonth
+                { 2017, 1, 2017, 1, 1 },
+                { 1, 365, 1, 12, 31 },
+                { 0, 32, 1, 2, 1 },
+                { -100, 1, 101, 1, 1 },
+                { 2000, 61, 2000, 3, 1 },
+                { 2000, 366, 2000, 12, 31 },
+                { Year.MAX_VALUE, 365, Year.MAX_VALUE, 12, 31 },
+                { Year.MIN_VALUE, 365, -Year.MIN_VALUE + 1, 12, 31 },
+        };
+
+        for (int[] values : allValues) {
+            LocalDate localDate = IsoChronology.INSTANCE.dateYearDay(values[0], values[1]);
+            IsoEra expectedEra = values[0] <= 0 ? IsoEra.BCE : IsoEra.CE;
+            assertEquals(expectedEra, localDate.getEra());
+            assertEquals(values[0], localDate.getYear());
+            assertEquals(values[1], localDate.getDayOfYear());
+            assertEquals(values[2], localDate.get(ChronoField.YEAR_OF_ERA));
+            assertEquals(values[3], localDate.getMonthValue());
+            assertEquals(values[4], localDate.getDayOfMonth());
+        }
+    }
+
+    @Test
+    public void test_dateYear_invalidValues() {
+        int[][] invalidValues = new int[][] {
+                { Year.MAX_VALUE + 1, 1 },
+                { Year.MIN_VALUE - 1, 1 },
+                { Integer.MAX_VALUE, 1 },
+                { Integer.MIN_VALUE, 1 },
+                { 2001, 366 },
+                { 2000, 367 },
+                { 2017, 0 },
+                { 2017, -1 },
+        };
+
+        for (int[] values : invalidValues) {
+            try {
+                LocalDate localDate = IsoChronology.INSTANCE.dateYearDay(values[0], values[1]);
+                fail(values[0] + "/" + values[1] + " should have failed, but produced "
+                        + localDate);
+            } catch (DateTimeException expected) {
+            }
+        }
+    }
+
+    @Test
+    public void test_range() {
+        for (ChronoField field : ChronoField.values()) {
+            // IsoChronology ranges should by definition be equal to the default ranges.
+            assertEquals(field.range(), IsoChronology.INSTANCE.range(field));
+        }
+    }
+
+    @Test
+    public void test_zonedDateTime() {
+        ZonedDateTime zonedDateTime = ZonedDateTime
+                .of(/* year */ 2017, /* month */ 4, /* dayOfMonth */ 1,
+                        /* hour */ 15, /* minute */ 14, /* second */ 13, /* nanoOfSecond */ 12,
+                        ZoneId.of("Europe/London"));
+
+        ZonedDateTime result = IsoChronology.INSTANCE
+                .zonedDateTime(zonedDateTime.toInstant(), zonedDateTime.getZone());
+        assertEquals(LocalDate.of(2017, 4, 1), result.toLocalDate());
+        assertEquals(LocalTime.of(15, 14, 13, 12), result.toLocalTime());
+        assertEquals(ZoneOffset.ofHours(1), result.getOffset());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_zonedDateTime_nullInstant() {
+        IsoChronology.INSTANCE.zonedDateTime(null, ZoneOffset.UTC);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_zonedDateTime_nullZone() {
+        IsoChronology.INSTANCE.zonedDateTime(Instant.EPOCH, null);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java
new file mode 100644
index 0000000..3c1f0cf
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.time.chrono.JapaneseChronology;
+import java.time.chrono.JapaneseDate;
+import java.time.chrono.JapaneseEra;
+import java.time.temporal.ChronoField;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link JapaneseChronology} and {@link JapaneseDate}.
+ *
+ * @see tck.java.time.chrono.TCKJapaneseChronology
+ */
+public class JapaneseChronologyTest {
+
+    @Test
+    public void test_zonedDateTime() {
+        ZonedDateTime zonedDateTime = ZonedDateTime
+                .of(2017, 4, 1, 15, 14, 13, 12, ZoneId.of("Europe/London"));
+
+        ChronoZonedDateTime<JapaneseDate> result = JapaneseChronology.INSTANCE
+                .zonedDateTime(zonedDateTime.toInstant(), zonedDateTime.getZone());
+        assertEquals(JapaneseDate.of(JapaneseEra.HEISEI, 29, 4, 1), result.toLocalDate());
+        assertEquals(LocalTime.of(15, 14, 13, 12), result.toLocalTime());
+        assertEquals(ZoneOffset.ofHours(1), result.getOffset());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_zonedDateTime_nullInstant() {
+        JapaneseChronology.INSTANCE.zonedDateTime(null, ZoneOffset.UTC);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_zonedDateTime_nullZone() {
+        JapaneseChronology.INSTANCE.zonedDateTime(Instant.EPOCH, null);
+    }
+
+    @Test
+    public void test_JapaneseDate_getChronology() {
+        assertSame(JapaneseChronology.INSTANCE, JapaneseDate.now().getChronology());
+    }
+
+    @Test
+    public void test_JapaneseDate_getEra() {
+        // pick the first january of the second year of each era, except for Meiji, because the
+        // first supported year in JapaneseChronology is Meiji 6.
+        assertEquals(JapaneseEra.MEIJI, JapaneseDate.from(LocalDate.of(1873, 1, 1)).getEra());
+        assertEquals(JapaneseEra.TAISHO, JapaneseDate.from(LocalDate.of(1913, 1, 1)).getEra());
+        assertEquals(JapaneseEra.SHOWA, JapaneseDate.from(LocalDate.of(1927, 1, 1)).getEra());
+        assertEquals(JapaneseEra.HEISEI, JapaneseDate.from(LocalDate.of(1990, 1, 1)).getEra());
+    }
+
+    @Test
+    public void test_JapaneseDate_isSupported_TemporalField() {
+        JapaneseDate date = JapaneseDate.now();
+        // all date based fields, except for the aligned week ones are supported.
+        assertEquals(false, date.isSupported(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH));
+        assertEquals(false, date.isSupported(ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR));
+        assertEquals(false, date.isSupported(ChronoField.ALIGNED_WEEK_OF_MONTH));
+        assertEquals(false, date.isSupported(ChronoField.ALIGNED_WEEK_OF_YEAR));
+        assertEquals(false, date.isSupported(ChronoField.AMPM_OF_DAY));
+        assertEquals(false, date.isSupported(ChronoField.CLOCK_HOUR_OF_AMPM));
+        assertEquals(false, date.isSupported(ChronoField.CLOCK_HOUR_OF_DAY));
+        assertEquals(true, date.isSupported(ChronoField.DAY_OF_MONTH));
+        assertEquals(true, date.isSupported(ChronoField.DAY_OF_WEEK));
+        assertEquals(true, date.isSupported(ChronoField.DAY_OF_YEAR));
+        assertEquals(true, date.isSupported(ChronoField.EPOCH_DAY));
+        assertEquals(true, date.isSupported(ChronoField.ERA));
+        assertEquals(false, date.isSupported(ChronoField.HOUR_OF_AMPM));
+        assertEquals(false, date.isSupported(ChronoField.HOUR_OF_DAY));
+        assertEquals(false, date.isSupported(ChronoField.INSTANT_SECONDS));
+        assertEquals(false, date.isSupported(ChronoField.MICRO_OF_DAY));
+        assertEquals(false, date.isSupported(ChronoField.MICRO_OF_SECOND));
+        assertEquals(false, date.isSupported(ChronoField.MILLI_OF_DAY));
+        assertEquals(false, date.isSupported(ChronoField.MILLI_OF_SECOND));
+        assertEquals(false, date.isSupported(ChronoField.MINUTE_OF_DAY));
+        assertEquals(false, date.isSupported(ChronoField.MINUTE_OF_HOUR));
+        assertEquals(true, date.isSupported(ChronoField.MONTH_OF_YEAR));
+        assertEquals(false, date.isSupported(ChronoField.NANO_OF_DAY));
+        assertEquals(false, date.isSupported(ChronoField.NANO_OF_SECOND));
+        assertEquals(false, date.isSupported(ChronoField.OFFSET_SECONDS));
+        assertEquals(true, date.isSupported(ChronoField.PROLEPTIC_MONTH));
+        assertEquals(false, date.isSupported(ChronoField.SECOND_OF_DAY));
+        assertEquals(false, date.isSupported(ChronoField.SECOND_OF_MINUTE));
+        assertEquals(true, date.isSupported(ChronoField.YEAR));
+        assertEquals(true, date.isSupported(ChronoField.YEAR_OF_ERA));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java
new file mode 100644
index 0000000..4d9e7bc
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.chrono.MinguoChronology;
+import java.time.chrono.MinguoDate;
+import java.time.chrono.MinguoEra;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ValueRange;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link MinguoChronology} and {@link MinguoDate}.
+ *
+ * @see tck.java.time.chrono.TCKMinguoChronology
+ */
+public class MinguoChronologyTest {
+
+    // year 1 in Minguo calendar is 1912 in ISO calendar.
+    private static final int YEARS_BEHIND = 1911;
+
+    @Test
+    public void test_range() {
+        for (ChronoField field : ChronoField.values()) {
+            ValueRange expected;
+            switch (field) {
+                case PROLEPTIC_MONTH:
+                    // Proleptic month values are shifted by YEARS_BEHIND * 12.
+                    expected = ValueRange.of(
+                            ChronoField.PROLEPTIC_MONTH.range().getMinimum() - YEARS_BEHIND * 12L,
+                            ChronoField.PROLEPTIC_MONTH.range().getMaximum() - YEARS_BEHIND * 12L);
+                    break;
+                case YEAR_OF_ERA:
+                    // range for era ROC is 1..<yearRange.max - OFFSET>
+                    // range for era before ROC is 1..<-yearRange.min + 1 + OFFSET>
+                    expected = ValueRange.of(1, ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND,
+                            -ChronoField.YEAR.range().getMinimum() + 1 + YEARS_BEHIND);
+                    break;
+                case YEAR:
+                    // Proleptic year values are shifted by YEAR.
+                    expected = ValueRange.of(ChronoField.YEAR.range().getMinimum() - YEARS_BEHIND,
+                            ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND);
+                    break;
+                default:
+                    // All other fields have the same ranges as ISO.
+                    expected = field.range();
+                    break;
+            }
+            assertEquals("Range of " + field, expected, MinguoChronology.INSTANCE.range(field));
+        }
+    }
+
+    @Test
+    public void test_MinguoDate_getChronology() {
+        assertSame(MinguoChronology.INSTANCE, MinguoDate.now().getChronology());
+    }
+
+    @Test
+    public void test_MinguoDate_getEra() {
+        assertEquals(MinguoEra.BEFORE_ROC, MinguoDate.of(-1, 1, 1).getEra());
+        assertEquals(MinguoEra.ROC, MinguoDate.of(1, 1, 1).getEra());
+    }
+
+    @Test
+    public void test_MinguoDate_range() {
+        MinguoDate dates[] = new MinguoDate[] {
+                MinguoDate.from(LocalDate.of(2000, 2, 1)), //February of a leap year
+                MinguoDate.from(LocalDate.of(2001, 2, 1)), //February of a non-leap year
+                MinguoDate.of(1, 2, 3),
+                MinguoDate.of(4, 5, 6),
+                MinguoDate.of(-7, 8, 9)
+        };
+
+        for (MinguoDate date : dates) {
+            // only these three ChronoFields and YEAR_OF_ERA (below) have date-dependent ranges.
+            assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_MONTH),
+                    date.range(ChronoField.DAY_OF_MONTH));
+            assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_YEAR),
+                    date.range(ChronoField.DAY_OF_YEAR));
+            assertEquals(LocalDate.from(date).range(ChronoField.ALIGNED_WEEK_OF_MONTH),
+                    date.range(ChronoField.ALIGNED_WEEK_OF_MONTH));
+        }
+    }
+
+    @Test
+    public void test_MinguoDate_range_yeaOfEra() {
+        // YEAR_OF_ERA is the big difference to a LocalDate, all other ranges are the same.
+        assertEquals(ValueRange.of(1, ChronoField.YEAR.range().getMaximum() - YEARS_BEHIND),
+                MinguoDate.of(1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+        assertEquals(ValueRange.of(1, -ChronoField.YEAR.range().getMinimum() + 1 + YEARS_BEHIND),
+                MinguoDate.of(-1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+    }
+
+    @Test
+    public void test_MinguoDate_getLong() {
+        MinguoDate date = MinguoDate.of(10, 2, 5);
+        assertEquals(10, date.getLong(ChronoField.YEAR_OF_ERA));
+        assertEquals(10, date.getLong(ChronoField.YEAR));
+        assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR));
+        assertEquals(10*12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH));
+        assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH));
+        assertEquals(31 + 5, date.getLong(ChronoField.DAY_OF_YEAR));
+        assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java
new file mode 100644
index 0000000..20e048e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistChronologyTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.time.chrono;
+
+import org.junit.Test;
+import java.time.LocalDate;
+import java.time.chrono.ThaiBuddhistDate;
+import java.time.chrono.ThaiBuddhistChronology;
+import java.time.chrono.ThaiBuddhistEra;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ValueRange;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Additional tests for {@link ThaiBuddhistDate}.
+ *
+ * @see tck.java.time.chrono.TCKThaiBuddhistChronology
+ */
+public class ThaiBuddhistChronologyTest {
+
+    // year 2543 in Thai Buddhist calendar is 2000 in ISO calendar.
+    private static final int YEARS_AHEAD = 543;
+
+    @Test
+    public void test_range() {
+        for (ChronoField field : ChronoField.values()) {
+            ValueRange expected;
+            switch (field) {
+                case PROLEPTIC_MONTH:
+                    // Proleptic month values are shifted by YEARS_AHEAD * 12.
+                    expected = ValueRange.of(
+                            ChronoField.PROLEPTIC_MONTH.range().getMinimum() + YEARS_AHEAD * 12L,
+                            ChronoField.PROLEPTIC_MONTH.range().getMaximum() + YEARS_AHEAD * 12L);
+                    break;
+                case YEAR_OF_ERA:
+                    // range for era BE is 1..<yearRange.max - OFFSET>
+                    // range for era before BE is 1..<-yearRange.min + 1 + OFFSET>
+                    expected = ValueRange
+                            .of(1, -ChronoField.YEAR.range().getMinimum() + 1 - YEARS_AHEAD,
+                                    ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD);
+                    break;
+                case YEAR:
+                    // Proleptic year values are shifted by YEAR.
+                    expected = ValueRange.of(ChronoField.YEAR.range().getMinimum() + YEARS_AHEAD,
+                            ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD);
+                    break;
+                default:
+                    // All other fields have the same ranges as ISO.
+                    expected = field.range();
+                    break;
+            }
+            assertEquals("Range of " + field, expected,
+                    ThaiBuddhistChronology.INSTANCE.range(field));
+        }
+    }
+
+    @Test
+    public void test_ThaiBuddhistDate_getChronology() {
+        assertSame(ThaiBuddhistChronology.INSTANCE, ThaiBuddhistDate.now().getChronology());
+    }
+
+    @Test
+    public void test_ThaiBuddhistDate_getEra() {
+        assertEquals(ThaiBuddhistEra.BEFORE_BE, ThaiBuddhistDate.of(-1, 1, 1).getEra());
+        assertEquals(ThaiBuddhistEra.BE, ThaiBuddhistDate.of(1, 1, 1).getEra());
+    }
+
+    @Test
+    public void test_ThaiBuddhistDate_range() {
+        ThaiBuddhistDate dates[] = new ThaiBuddhistDate[] {
+                ThaiBuddhistDate.from(LocalDate.of(2000, 2, 1)), //February of a leap year
+                ThaiBuddhistDate.from(LocalDate.of(2001, 2, 1)), //February of a non-leap year
+                ThaiBuddhistDate.of(1, 2, 3),
+                ThaiBuddhistDate.of(4, 5, 6),
+                ThaiBuddhistDate.of(-7, 8, 9)
+        };
+
+        for (ThaiBuddhistDate date : dates) {
+            // only these three ChronoFields and YEAR_OF_ERA (below) have date-dependent ranges.
+            assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_MONTH),
+                    date.range(ChronoField.DAY_OF_MONTH));
+            assertEquals(LocalDate.from(date).range(ChronoField.DAY_OF_YEAR),
+                    date.range(ChronoField.DAY_OF_YEAR));
+            assertEquals(LocalDate.from(date).range(ChronoField.ALIGNED_WEEK_OF_MONTH),
+                    date.range(ChronoField.ALIGNED_WEEK_OF_MONTH));
+        }
+    }
+
+    @Test
+    public void test_ThaiBuddhistDate_range_yeaOfEra() {
+        // YEAR_OF_ERA is the big difference to a LocalDate, all other ranges are the same.
+        assertEquals(ValueRange.of(1, ChronoField.YEAR.range().getMaximum() + YEARS_AHEAD),
+                ThaiBuddhistDate.of(1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+        assertEquals(ValueRange.of(1, -ChronoField.YEAR.range().getMinimum() + 1 - YEARS_AHEAD),
+                ThaiBuddhistDate.of(-1, 1, 1).range(ChronoField.YEAR_OF_ERA));
+    }
+
+    @Test
+    public void test_ThaiBuddhistDate_getLong() {
+        ThaiBuddhistDate date = ThaiBuddhistDate.of(10, 2, 5);
+        assertEquals(10, date.getLong(ChronoField.YEAR_OF_ERA));
+        assertEquals(10, date.getLong(ChronoField.YEAR));
+        assertEquals(2, date.getLong(ChronoField.MONTH_OF_YEAR));
+        assertEquals(10 * 12 + 2 - 1, date.getLong(ChronoField.PROLEPTIC_MONTH));
+        assertEquals(5, date.getLong(ChronoField.DAY_OF_MONTH));
+        assertEquals(31 + 5, date.getLong(ChronoField.DAY_OF_YEAR));
+        assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java
new file mode 100644
index 0000000..215f483
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterBuilderTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.time.format;
+
+import org.junit.Test;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.Month;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.TemporalQueries;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Additional tests for {@link DateTimeFormatterBuilder}.
+ *
+ * @see tck.java.time.format.TCKDateTimeFormatterBuilder
+ * @see test.java.time.format.TestDateTimeFormatterBuilder
+ */
+public class DateTimeFormatterBuilderTest {
+
+    @Test
+    public void test_append_DateTimeFormatter() {
+        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
+                .appendLiteral('<').append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('>')
+                .toFormatter(Locale.ROOT);
+        assertEquals("<2000-12-31>", formatter.format(LocalDate.of(2000, Month.DECEMBER, 31)));
+    }
+
+    @Test
+    public void test_appendZoneRegionId_format() {
+        DateTimeFormatter formatter =
+                new DateTimeFormatterBuilder().appendZoneRegionId().toFormatter();
+
+        assertEquals("Europe/London",
+                formatter.format(ZonedDateTime.now(ZoneId.of("Europe/London"))));
+        assertEquals("UTC",
+                formatter.format(ZonedDateTime.now(ZoneId.of("UTC"))));
+    }
+
+    @Test
+    public void test_appendZoneRegionId_format_offset() {
+        DateTimeFormatter formatter =
+                new DateTimeFormatterBuilder().appendZoneRegionId().toFormatter();
+
+        try {
+            formatter.format(OffsetDateTime.now(ZoneOffset.UTC));
+            fail("Formatted ZoneOffset using appendZoneRegionId formatter");
+        } catch (DateTimeException expected) {
+        }
+    }
+
+    @Test
+    public void test_appendZoneRegionId_parse() {
+        DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendZoneRegionId()
+                .toFormatter();
+
+        assertEquals(ZoneId.of("Europe/London"),
+                formatter.parse("Europe/London").query(TemporalQueries.zoneId()));
+        assertEquals(ZoneId.of("UTC"),
+                formatter.parse("UTC").query(TemporalQueries.zoneId()));
+        assertEquals(ZoneId.of("GMT+1"),
+                formatter.parse("GMT+01:00").query(TemporalQueries.zoneId()));
+        // Note that the JavaDoc for appendZoneRegionId() suggests that this should return a
+        // ZoneOffset, but that documentation seems to be wrong (see http://b/35665981).
+        assertEquals(ZoneId.of("UTC+01:00"),
+                formatter.parse("UTC+01:00").query(TemporalQueries.zoneId()));
+        // Parsing a "bare metal" offset without prefix will return a ZoneOffset.
+        assertEquals(ZoneOffset.ofHours(1),
+                formatter.parse("+01:00").query(TemporalQueries.zoneId()));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
new file mode 100644
index 0000000..0e57178
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.time.format;
+
+import org.junit.Test;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DecimalStyle;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+/**
+ * Additional tests for {@link DateTimeFormatter}.
+ *
+ * @see tck.java.time.format.TCKDateTimeFormatter
+ * @see test.java.time.format.TestDateTimeFormatter
+ */
+public class DateTimeFormatterTest {
+
+    @Test
+    public void test_getDecimalStyle() {
+        Locale arLocale = Locale.forLanguageTag("ar");
+        DateTimeFormatter[] formatters = new DateTimeFormatter[] {
+                DateTimeFormatter.ISO_DATE,
+                DateTimeFormatter.RFC_1123_DATE_TIME,
+                new DateTimeFormatterBuilder().toFormatter(),
+                new DateTimeFormatterBuilder().toFormatter(Locale.ROOT),
+                new DateTimeFormatterBuilder().toFormatter(Locale.ENGLISH),
+                new DateTimeFormatterBuilder().toFormatter(arLocale),
+        };
+
+        DecimalStyle arDecimalStyle = DecimalStyle.of(arLocale);
+        // Verify that the Locale ar returns a DecimalStyle other than STANDARD.
+        assertNotEquals(DecimalStyle.STANDARD, arDecimalStyle);
+
+        for (DateTimeFormatter formatter : formatters) {
+            // All DateTimeFormatters should use the standard style, unless explicitly changed.
+            assertEquals(formatter.toString(), DecimalStyle.STANDARD, formatter.getDecimalStyle());
+
+            DateTimeFormatter arStyleFormatter = formatter.withDecimalStyle(arDecimalStyle);
+            assertEquals(arStyleFormatter.toString(),
+                    arDecimalStyle, arStyleFormatter.getDecimalStyle());
+
+            // Verify that calling withDecimalStyle() doesn't modify the original formatter.
+            assertEquals(formatter.toString(), DecimalStyle.STANDARD, formatter.getDecimalStyle());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java
new file mode 100644
index 0000000..edcd782
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeParseExceptionTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.time.format;
+
+import org.junit.Test;
+import java.time.format.DateTimeParseException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link DateTimeParseException}.
+ */
+public class DateTimeParseExceptionTest {
+    @Test
+    public void test_constructor_message_parsedData_errorIndex() {
+        DateTimeParseException ex =
+                new DateTimeParseException("message", new StringBuilder("parsedData"), 42);
+        assertEquals("message", ex.getMessage());
+        assertEquals("parsedData", ex.getParsedString());
+        assertEquals(42, ex.getErrorIndex());
+        assertNull(ex.getCause());
+    }
+
+    @Test
+    public void test_constructor_message_parsedData_errorIndex_cause() {
+        Throwable cause = new Exception();
+        DateTimeParseException ex =
+                new DateTimeParseException("message", new StringBuilder("parsedData"), 42, cause);
+        assertEquals("message", ex.getMessage());
+        assertEquals("parsedData", ex.getParsedString());
+        assertEquals(42, ex.getErrorIndex());
+        assertSame(cause, ex.getCause());
+    }
+
+
+}
diff --git a/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java b/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java
new file mode 100644
index 0000000..fa155df
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/temporal/UnsupportedTemporalTypeExceptionTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.time.temporal;
+
+import org.junit.Test;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link UnsupportedTemporalTypeException}.
+ */
+public class UnsupportedTemporalTypeExceptionTest {
+    @Test
+    public void test_constructor_message() {
+        UnsupportedTemporalTypeException ex = new UnsupportedTemporalTypeException("message");
+        assertEquals("message", ex.getMessage());
+        assertNull(ex.getCause());
+    }
+
+    @Test
+    public void test_constructor_message_cause() {
+        Throwable cause = new Exception();
+        UnsupportedTemporalTypeException ex =
+                new UnsupportedTemporalTypeException("message", cause);
+        assertEquals("message", ex.getMessage());
+        assertSame(cause, ex.getCause());
+    }
+
+
+}
diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java
new file mode 100644
index 0000000..6094afb
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/zone/ZoneOffsetTransitionTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.time.zone;
+
+import org.junit.Test;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.zone.ZoneOffsetTransition;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Additional tests for {@link ZoneOffsetTransition}.
+ *
+ * @see tck.java.time.zone.TCKZoneOffsetTransition
+ */
+public class ZoneOffsetTransitionTest {
+
+    @Test
+    public void test_toEpochSeconds() {
+        LocalDateTime time = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0);
+        ZoneOffset offsetP1 = ZoneOffset.ofHours(1);
+        ZoneOffset offsetP2 = ZoneOffset.ofHours(2);
+        ZoneOffsetTransition transition = ZoneOffsetTransition.of(time,
+                /* offsetBefore */ offsetP1, /* offsetAfter */ offsetP2);
+        // toEpochSeconds must match the toEpochSeconds of the original time at the "offset before".
+        assertEquals(
+                OffsetDateTime.of(time, offsetP1).toEpochSecond(),
+                transition.toEpochSecond());
+
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java
new file mode 100644
index 0000000..4005175
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/zone/ZoneRulesExceptionTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.time.zone;
+
+import org.junit.Test;
+import java.time.zone.ZoneRulesException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for {@link ZoneRulesException}.
+ */
+public class ZoneRulesExceptionTest {
+
+    @Test
+    public void test_constructor_message() {
+        ZoneRulesException ex = new ZoneRulesException("message");
+        assertEquals("message", ex.getMessage());
+        assertNull(ex.getCause());
+    }
+
+    @Test
+    public void test_constructor_message_cause() {
+        Throwable cause = new Exception();
+        ZoneRulesException ex = new ZoneRulesException("message", cause);
+        assertEquals("message", ex.getMessage());
+        assertSame(cause, ex.getCause());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java b/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java
new file mode 100644
index 0000000..54b5bba
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/zone/ZoneRulesTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.time.zone;
+
+import org.junit.Test;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.time.zone.ZoneRules;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Additional tests for {@link ZoneRules}.
+ *
+ * @see tck.java.time.zone.TCKZoneRules
+ */
+public class ZoneRulesTest {
+
+    @Test
+    public void test_of_ZoneOffset() {
+        ZoneOffset offset = ZoneOffset.MIN;
+        ZoneRules zoneRules = ZoneRules.of(offset);
+
+        assertEquals(Collections.emptyList(), zoneRules.getTransitionRules());
+        assertEquals(Collections.emptyList(), zoneRules.getTransitions());
+        assertNull(zoneRules.nextTransition(Instant.MIN));
+
+        // Check various offsets at a bunch of instants, as they should be constant.
+        Instant[] instants = new Instant[] {
+                LocalDateTime.MIN.toInstant(offset),
+                Instant.EPOCH,
+                LocalDateTime.of(2000, Month.JANUARY, 1, 1, 1).toInstant(ZoneOffset.UTC),
+                Instant.now(),
+                LocalDateTime.MAX.toInstant(offset),
+        };
+
+        for (Instant instant : instants) {
+            assertEquals(Duration.ZERO, zoneRules.getDaylightSavings(instant));
+            assertEquals(offset, zoneRules.getOffset(instant));
+            assertEquals(offset, zoneRules.getStandardOffset(instant));
+            LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, offset);
+            assertNull(zoneRules.getTransition(localDateTime));
+            assertEquals(Collections.singletonList(offset),
+                    zoneRules.getValidOffsets(localDateTime));
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_of_ZoneOffset_null() {
+        ZoneRules.of(null);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java b/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java
new file mode 100644
index 0000000..5e4ac96
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/CalendarBuilderTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.util;
+
+import org.junit.Before;
+import org.junit.Test;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests {@link java.util.Calendar.Builder}.
+ */
+public class CalendarBuilderTest {
+
+
+    @Test
+    public void test_default_values() {
+        Calendar.Builder builder = new Calendar.Builder();
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setCalendarType_iso8601() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setCalendarType("iso8601");
+        // ISO 8601 represents a gregorian calendar with a specific configuration
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.setGregorianChange(new Date(Long.MIN_VALUE));
+        expected.setFirstDayOfWeek(Calendar.MONDAY);
+        expected.setMinimalDaysInFirstWeek(4);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setCalendarType_invalid() {
+        Calendar.Builder builder = new Calendar.Builder();
+        try {
+            builder.setCalendarType(null);
+            fail("Should have thrown NPE");
+        } catch (NullPointerException expected) {}
+
+        for (String unsupported : new String[] { "buddhist", "japanese", "notACalendarType" }) {
+            try {
+                // not supported
+                builder.setCalendarType(unsupported);
+                fail("Unsupported calendar type " + unsupported + " should have thrown.");
+            } catch (IllegalArgumentException expected) {}
+        }
+    }
+
+    @Test
+    public void test_setCalendarType_reset() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setCalendarType("gregorian");
+        try {
+            builder.setCalendarType("iso8601");
+            fail("Should not accept second setCalendarType() call");
+        } catch (IllegalStateException expected) {}
+    }
+
+    @Test
+    public void test_setDate() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setDate(2000, Calendar.FEBRUARY, 3);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.set(2000, Calendar.FEBRUARY, 3);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setTimeOfDay() {
+        Calendar.Builder builder = new Calendar.Builder();
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        builder.setTimeOfDay(10, 11, 12);
+        expected.set(1970, Calendar.JANUARY, 1, 10, 11, 12);
+        assertEquals(expected, builder.build());
+        builder.setTimeOfDay(10, 11, 12, 13);
+        expected.set(Calendar.MILLISECOND, 13);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setWeekDate() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setWeekDate(1, 2000, Calendar.TUESDAY);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.setWeekDate(1, 2000, Calendar.TUESDAY);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setLenient() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.set(Calendar.HOUR_OF_DAY, 25);
+        builder.setLenient(false);
+        try {
+            builder.build();
+            fail("Should have failed to build.");
+        } catch (IllegalArgumentException expected) {}
+        builder.setLenient(true);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.setLenient(true);
+        expected.set(Calendar.HOUR_OF_DAY, 25);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setLocale() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setLocale(Locale.GERMANY);
+        GregorianCalendar expected = new GregorianCalendar(Locale.GERMANY);
+        expected.clear();
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setLocale_thTH() {
+        // See http://b/35138741
+        Calendar.Builder builder = new Calendar.Builder();
+        Locale th = new Locale("th", "TH");
+        builder.setLocale(th);
+        GregorianCalendar expected = new GregorianCalendar(th);
+        expected.clear();
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_set() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.set(Calendar.YEAR, 2000);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.set(Calendar.YEAR, 2000);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void test_set_negative_field() {
+        new Calendar.Builder().set(-1, 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void test_set_field_too_high() {
+        new Calendar.Builder().set(Calendar.FIELD_COUNT, 1);
+    }
+
+    @Test
+    public void test_set_after_setInstant() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setInstant(0L);
+        try {
+            builder.set(Calendar.YEAR, 2000);
+            fail("Setting a field after setInstant should fail.");
+        } catch (IllegalStateException expected) {}
+    }
+
+    @Test
+    public void test_setFields() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setFields(Calendar.YEAR, 2000, Calendar.MONTH, Calendar.FEBRUARY);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.set(Calendar.YEAR, 2000);
+        expected.set(Calendar.MONTH, Calendar.FEBRUARY);
+        assertEquals(expected, builder.build());
+
+        // field values can be re-set and order of fields matter
+        builder.setFields(Calendar.DAY_OF_WEEK_IN_MONTH, 1,
+                Calendar.DAY_OF_MONTH, 20, // this will effectively be ignored
+                Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
+        expected.set(Calendar.DAY_OF_WEEK_IN_MONTH, 1);
+        expected.set(Calendar.DAY_OF_MONTH, 20);
+        expected.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
+        assertEquals(expected, builder.build());
+        // 20th February 2000 would have been a Sunday, but we set the DOW last.
+        assertEquals(Calendar.WEDNESDAY, builder.build().get(Calendar.DAY_OF_WEEK));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_setFields_null() {
+        new Calendar.Builder().setFields(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void test_setFields_oddNumberOfArguments() {
+        new Calendar.Builder().setFields(Calendar.YEAR);
+    }
+
+    @Test
+    public void test_setFields_after_setInstant() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setInstant(0L);
+        try {
+            builder.setFields(Calendar.YEAR, 2000);
+            fail("Setting a field after setInstant should fail.");
+        } catch (IllegalStateException expected) {}
+    }
+
+    @Test
+    public void test_setInstant() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setInstant(Long.MIN_VALUE);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.setTimeInMillis(Long.MIN_VALUE);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test
+    public void test_setInstant_after_set() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.set(Calendar.YEAR, 2000);
+        try {
+            builder.setInstant(0L);
+            fail("Setting the instant after setting a field should fail.");
+        } catch (IllegalStateException expected) {}
+    }
+
+    @Test
+    public void test_setInstant_Date() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setInstant(new Date(Long.MAX_VALUE));
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.setTimeInMillis(Long.MAX_VALUE);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_setInstant_Date_null() {
+        new Calendar.Builder().setInstant(null);
+    }
+
+    @Test
+    public void test_setTimeZone() {
+        TimeZone london = TimeZone.getTimeZone("Europe/London");
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setTimeZone(london);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.setTimeZone(london);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_setTimeZone_null() {
+        new Calendar.Builder().setTimeZone(null);
+    }
+
+    @Test
+    public void test_setWeekDefinition() {
+        Calendar.Builder builder = new Calendar.Builder();
+        builder.setWeekDefinition(Calendar.TUESDAY, 7);
+        GregorianCalendar expected = new GregorianCalendar();
+        expected.clear();
+        expected.setFirstDayOfWeek(Calendar.TUESDAY);
+        expected.setMinimalDaysInFirstWeek(7);
+        assertEquals(expected, builder.build());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void test_setWeekDefinition_invalid_first_dow() {
+        new Calendar.Builder().setWeekDefinition(-1, 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void test_setWeekDefinition_invalid_minimum_days() {
+        new Calendar.Builder().setWeekDefinition(Calendar.WEDNESDAY, 8);
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java
index 6185f12..df86a38 100644
--- a/luni/src/test/java/libcore/java/util/DateTest.java
+++ b/luni/src/test/java/libcore/java/util/DateTest.java
@@ -81,49 +81,63 @@
     }
 
     /**
-     * The minimum long value below which {@link Instant#toEpochMilli()} will
-     * throw is not clearly documented. This test discovers if that minimum
-     * value ever changes, and also checks that it is also the minimum Instant
-     * (at a millisecond boundary) that can be converted to a Date.
+     * Test that conversion between Date and Instant works when the
+     * Instant is based on a millisecond value (and thus can be
+     * represented as a Date).
      */
-    public void test_convertFromInstant_lowerBound() {
-        // smallest millisecond Instant that can be converted to Date
-        long minConvertible = -9223372036854775000L;
+    public void test_convertFromAndToInstant_milliseconds() {
+        check_convertFromAndToInstant_milliseconds(Long.MIN_VALUE);
+        check_convertFromAndToInstant_milliseconds(Long.MAX_VALUE);
 
-        // show that this value is < 1 sec away from Long.MIN_VALUE
-        assertEquals(Long.MIN_VALUE + 808, minConvertible);
-
-        Instant inBound = Instant.ofEpochMilli(minConvertible);
-        assertEquals(new Date(minConvertible), Date.from(inBound));
-        assertEquals(minConvertible, inBound.toEpochMilli());
-
-        Instant outOfBound = Instant.ofEpochMilli(minConvertible - 1);
-        try {
-            Date.from(outOfBound);
-            fail();
-        } catch (IllegalArgumentException expected) {
-            assertEquals(ArithmeticException.class, expected.getCause().getClass());
-        }
-
-        try {
-            outOfBound.toEpochMilli();
-            fail();
-        } catch (ArithmeticException expected) {
-
-        }
+        check_convertFromAndToInstant_milliseconds(-1);
+        check_convertFromAndToInstant_milliseconds(0);
+        check_convertFromAndToInstant_milliseconds(123456789);
     }
 
-    public void test_convertFromInstant_upperBound() {
-        Date.from(Instant.ofEpochMilli(Long.MAX_VALUE));
+    private static void check_convertFromAndToInstant_milliseconds(long millis) {
+        assertEquals(new Date(millis), Date.from(Instant.ofEpochMilli(millis)));
+        assertEquals(new Date(millis).toInstant(), Instant.ofEpochMilli(millis));
+    }
 
-        Date.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 0));
-        Date.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 999999999));
-        Instant outOfBound = Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1, 0);
+    /**
+     * Checks the minimum/maximum Instant values (based on seconds and
+     * nanos) that can be converted to a Date, i.e. that can be converted
+     * to milliseconds without overflowing a long. Note that the rounding
+     * is such that the lower bound is exactly Long.MIN_VALUE msec whereas
+     * the upper bound is 999,999 nanos beyond Long.MAX_VALUE msec. This
+     * makes some sense in that the magnitude of the upper/lower bound
+     * nanos differ only by 1, just like the magnitude of Long.MIN_VALUE /
+     * MAX_VALUE differ only by 1.
+     */
+    public void test_convertFromInstant_secondsAndNanos() {
+        // Documentation for how the below bounds relate to long boundaries for milliseconds
+        assertEquals(-808, Long.MIN_VALUE % 1000);
+        assertEquals(807, Long.MAX_VALUE % 1000);
+
+        // Lower bound
+        long minSecond = Long.MIN_VALUE / 1000;
+        Date.from(Instant.ofEpochSecond(minSecond));
+        // This instant exactly corresponds to Long.MIN_VALUE msec because
+        // Long.MIN_VALUE % 1000 == -808 == (-1000 + 192)
+        Date.from(Instant.ofEpochSecond(minSecond - 1, 192000000));
+        assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 0));
+        assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(minSecond - 1, 191999999));
+
+        // Upper bound
+        long maxSecond = Long.MAX_VALUE / 1000;
+        Date.from(Instant.ofEpochSecond(maxSecond, 0));
+        // This Instant is 999,999 nanos beyond Long.MAX_VALUE msec because
+        // (Long.MAX_VALUE % 1000) == 807
+        Date.from(Instant.ofEpochSecond(maxSecond, 807999999));
+        assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond + 1, 0));
+        assertArithmeticOverflowDateFrom(Instant.ofEpochSecond(maxSecond, 808000000));
+    }
+
+    private static void assertArithmeticOverflowDateFrom(Instant instant) {
         try {
-            Date.from(outOfBound);
-            fail();
+            Date.from(instant);
+            fail(instant + " should not have been convertible to Date");
         } catch (IllegalArgumentException expected) {
-            assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
     }
 
diff --git a/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java b/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java
index 750b8db..3345ed5 100644
--- a/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/GregorianCalendarTest.java
@@ -16,6 +16,9 @@
 
 package libcore.java.util;
 
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.Calendar;
 import java.util.Date;
@@ -297,6 +300,21 @@
         assertEquals(3600 * 1000, calendar.getTimeZone().getRawOffset()); // in milliseconds
     }
 
+    public void test_fromZonedDateTime_invalidValues() {
+        ZoneId gmt = ZoneId.of("GMT");
+        ZonedDateTime[] invalidValues = {
+                ZonedDateTime.of(LocalDateTime.MAX, gmt),
+                ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.MAX_VALUE).plusMillis(1), gmt),
+                ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.MIN_VALUE).minusMillis(1), gmt),
+                ZonedDateTime.of(LocalDateTime.MAX, gmt) };
+        for (ZonedDateTime invalidValue : invalidValues) {
+            try {
+                GregorianCalendar.from(invalidValue);
+                fail("GregorianCalendar.from() should have failed with " + invalidValue);
+            } catch (IllegalArgumentException expected) {}
+        }
+    }
+
     public void test_toZonedDateTime() {
         TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris");
         GregorianCalendar calendar = new GregorianCalendar(timeZone);
diff --git a/luni/src/test/java/libcore/java/util/HashtableTest.java b/luni/src/test/java/libcore/java/util/HashtableTest.java
index 4a61147..b7a3202 100644
--- a/luni/src/test/java/libcore/java/util/HashtableTest.java
+++ b/luni/src/test/java/libcore/java/util/HashtableTest.java
@@ -20,6 +20,11 @@
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ByteArrayInputStream;
+import java.lang.reflect.Field;
 
 public class HashtableTest extends junit.framework.TestCase {
 
@@ -130,4 +135,30 @@
         } catch (ClassNotFoundException expected) {
         }
     }
+
+    public void test_deserializedArrayLength() throws Exception {
+        final float loadFactor = 0.75f;
+        final int entriesCount = 100;
+        // Create table
+        Hashtable<Integer, Integer> hashtable1 = new Hashtable<>(1, loadFactor);
+        for (int i = 0; i < entriesCount; i++) {
+            hashtable1.put(i, 1);
+        }
+
+        // Serialize and deserialize
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(hashtable1);
+        }
+        Hashtable<Integer, Integer> hashtable2 =
+                (Hashtable<Integer, Integer>) new ObjectInputStream(
+                    new ByteArrayInputStream(bos.toByteArray())).readObject();
+
+        // Check that table size is >= min expected size. Due to a bug in
+        // Hashtable deserialization this wasn't the case.
+        Field tableField = Hashtable.class.getDeclaredField("table");
+        tableField.setAccessible(true);
+        Object[] table2 = (Object[]) tableField.get(hashtable2);
+        assertTrue(table2.length >= (entriesCount / loadFactor));
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index f24e0b7..2b3347c 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -157,6 +157,9 @@
         if (algorithm.startsWith("AES/")) {
             return "AES";
         }
+        if (algorithm.startsWith("AES_128/") || algorithm.startsWith("AES_256/")) {
+            return "AES";
+        }
         if (algorithm.equals("GCM")) {
             return "AES";
         }
@@ -273,6 +276,11 @@
                 key = skf.generateSecret(new PBEKeySpec("secret".toCharArray()));
             } else {
                 KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(algorithm));
+                if (algorithm.startsWith("AES_256/")) {
+                    // This is the 256-bit constrained version, so we have to switch from the
+                    // default of 128-bit keys.
+                    kg.init(256);
+                }
                 key = kg.generateKey();
             }
         } catch (Exception e) {
@@ -333,6 +341,20 @@
         setExpectedBlockSize("AES/OFB/PKCS5PADDING", 16);
         setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16);
         setExpectedBlockSize("AES/OFB/NOPADDING", 16);
+        setExpectedBlockSize("AES_128/CBC/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_128/CBC/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_128/CBC/NOPADDING", 16);
+        setExpectedBlockSize("AES_128/ECB/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_128/ECB/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_128/ECB/NOPADDING", 16);
+        setExpectedBlockSize("AES_128/GCM/NOPADDING", 16);
+        setExpectedBlockSize("AES_256/CBC/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_256/CBC/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_256/CBC/NOPADDING", 16);
+        setExpectedBlockSize("AES_256/ECB/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_256/ECB/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_256/ECB/NOPADDING", 16);
+        setExpectedBlockSize("AES_256/GCM/NOPADDING", 16);
         setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
         setExpectedBlockSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
         setExpectedBlockSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -490,6 +512,10 @@
         setExpectedOutputSize("AES/CTS/NOPADDING", 0);
         setExpectedOutputSize("AES/ECB/NOPADDING", 0);
         setExpectedOutputSize("AES/OFB/NOPADDING", 0);
+        setExpectedOutputSize("AES_128/CBC/NOPADDING", 0);
+        setExpectedOutputSize("AES_128/ECB/NOPADDING", 0);
+        setExpectedOutputSize("AES_256/CBC/NOPADDING", 0);
+        setExpectedOutputSize("AES_256/ECB/NOPADDING", 0);
 
         setExpectedOutputSize("AES", Cipher.ENCRYPT_MODE, 16);
         setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
@@ -505,6 +531,16 @@
         setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
         setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
         setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
+        setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
         setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
         setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
         setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -538,6 +574,16 @@
         setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
         setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
         setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
         setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
         setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
         setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
@@ -771,7 +817,11 @@
         if (algorithm.equals("AES")
             || algorithm.equals("AES/CBC/NOPADDING")
             || algorithm.equals("AES/CTS/NOPADDING")
-            || algorithm.equals("AES/ECB/NOPADDING")) {
+            || algorithm.equals("AES/ECB/NOPADDING")
+            || algorithm.equals("AES_128/CBC/NOPADDING")
+            || algorithm.equals("AES_128/ECB/NOPADDING")
+            || algorithm.equals("AES_256/CBC/NOPADDING")
+            || algorithm.equals("AES_256/ECB/NOPADDING")) {
             return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
         }
         if (algorithm.equals("DESEDE")
@@ -787,7 +837,11 @@
         if (algorithm.equals("AES")
             || algorithm.equals("AES/CBC/NOPADDING")
             || algorithm.equals("AES/CTS/NOPADDING")
-            || algorithm.equals("AES/ECB/NOPADDING")) {
+            || algorithm.equals("AES/ECB/NOPADDING")
+            || algorithm.equals("AES_128/CBC/NOPADDING")
+            || algorithm.equals("AES_128/ECB/NOPADDING")
+            || algorithm.equals("AES_256/CBC/NOPADDING")
+            || algorithm.equals("AES_256/ECB/NOPADDING")) {
             return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
         }
         if (algorithm.equals("DESEDE")
@@ -808,7 +862,9 @@
             new SecureRandom().nextBytes(salt);
             return new PBEParameterSpec(salt, 1024);
         }
-        if (algorithm.equals("AES/GCM/NOPADDING")) {
+        if (algorithm.equals("AES/GCM/NOPADDING")
+            || algorithm.equals("AES_128/GCM/NOPADDING")
+            || algorithm.equals("AES_256/GCM/NOPADDING")) {
             final byte[] iv = new byte[12];
             new SecureRandom().nextBytes(iv);
             return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
@@ -819,7 +875,13 @@
             || algorithm.equals("AES/CFB/NOPADDING")
             || algorithm.equals("AES/CTR/NOPADDING")
             || algorithm.equals("AES/CTS/NOPADDING")
-            || algorithm.equals("AES/OFB/NOPADDING")) {
+            || algorithm.equals("AES/OFB/NOPADDING")
+            || algorithm.equals("AES_128/CBC/NOPADDING")
+            || algorithm.equals("AES_128/CBC/PKCS5PADDING")
+            || algorithm.equals("AES_128/CBC/PKCS7PADDING")
+            || algorithm.equals("AES_256/CBC/NOPADDING")
+            || algorithm.equals("AES_256/CBC/PKCS5PADDING")
+            || algorithm.equals("AES_256/CBC/PKCS7PADDING")) {
             final byte[] iv = new byte[16];
             new SecureRandom().nextBytes(iv);
             return new IvParameterSpec(iv);
@@ -849,7 +911,9 @@
         }
         byte[] iv = encryptCipher.getIV();
         if (iv != null) {
-            if ("AES/GCM/NOPADDING".equals(algorithm)) {
+            if ("AES/GCM/NOPADDING".equals(algorithm)
+                    || "AES_128/GCM/NOPADDING".equals(algorithm)
+                    || "AES_256/GCM/NOPADDING".equals(algorithm)) {
                 return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
             }
             return new IvParameterSpec(iv);
@@ -1269,7 +1333,9 @@
                     seenBaseCipherNames.add(algorithm);
                 } else {
                     final String baseCipherName = algorithm.substring(0, firstSlash);
-                    if (!seenBaseCipherNames.contains(baseCipherName)) {
+                    if (!seenBaseCipherNames.contains(baseCipherName)
+                            && !(baseCipherName.equals("AES_128")
+                                || baseCipherName.equals("AES_256"))) {
                         seenCiphersWithModeAndPadding.add(baseCipherName);
                     }
                     if (!"AndroidOpenSSL".equals(provider.getName())) {
@@ -1362,7 +1428,7 @@
         } catch (IllegalStateException expected) {
         }
 
-        // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs JCERSAPrivateKey)
+        // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs BCRSAPrivateKey)
         Key encryptKey = getEncryptKey(algorithm);
 
         final AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
@@ -1470,8 +1536,7 @@
 
         // Test wrapping a key.  Every cipher should be able to wrap. Except those that can't.
         /* Bouncycastle is broken for wrapping because getIV() fails. */
-        if (isSupportedForWrapping(algorithm)
-                && !algorithm.equals("AES/GCM/NOPADDING") && !providerName.equals("BC")) {
+        if (isSupportedForWrapping(algorithm) && !providerName.equals("BC")) {
             // Generate a small SecretKey for AES.
             KeyGenerator kg = KeyGenerator.getInstance("AES");
             kg.init(128);
@@ -4554,6 +4619,39 @@
         }
     }
 
+    public void testAES_keyConstrained() throws Exception {
+        Provider[] providers = Security.getProviders();
+        for (Provider p : providers) {
+            for (Provider.Service s : p.getServices()) {
+                if (s.getType().equals("Cipher")) {
+                    if (s.getAlgorithm().startsWith("AES_128/")) {
+                        Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
+                        assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
+                    } else if (s.getAlgorithm().startsWith("AES_256/")) {
+                        Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+                        assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean checkAES_keyConstraint(Cipher c, int keySize) throws Exception {
+        KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(c.getAlgorithm()));
+        kg.init(keySize);
+        SecretKey key = kg.generateKey();
+        try {
+            c.init(Cipher.ENCRYPT_MODE, key);
+            return true;
+        } catch (InvalidKeyException e) {
+            return false;
+        }
+    }
+
     /**
      * Several exceptions can be thrown by init. Check that in this case we throw the right one,
      * as the error could fall under the umbrella of other exceptions.
diff --git a/ojluni/src/main/java/java/lang/BootstrapMethodError.java b/ojluni/src/main/java/java/lang/BootstrapMethodError.java
new file mode 100644
index 0000000..a1509a0
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/BootstrapMethodError.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+/**
+ * Thrown to indicate that an {@code invokedynamic} instruction has
+ * failed to find its bootstrap method,
+ * or the bootstrap method has failed to provide a
+ * {@linkplain java.lang.invoke.CallSite call site} with a {@linkplain java.lang.invoke.CallSite#getTarget target}
+ * of the correct {@linkplain java.lang.invoke.MethodHandle#type method type}.
+ *
+ * @author John Rose, JSR 292 EG
+ * @since 1.7
+ */
+public class BootstrapMethodError extends LinkageError {
+    private static final long serialVersionUID = 292L;
+
+    /**
+     * Constructs a {@code BootstrapMethodError} with no detail message.
+     */
+    public BootstrapMethodError() {
+        super();
+    }
+
+    /**
+     * Constructs a {@code BootstrapMethodError} with the specified
+     * detail message.
+     *
+     * @param s the detail message.
+     */
+    public BootstrapMethodError(String s) {
+        super(s);
+    }
+
+    /**
+     * Constructs a {@code BootstrapMethodError} with the specified
+     * detail message and cause.
+     *
+     * @param s the detail message.
+     * @param cause the cause, may be {@code null}.
+     */
+    public BootstrapMethodError(String s, Throwable cause) {
+        super(s, cause);
+    }
+
+    /**
+     * Constructs a {@code BootstrapMethodError} with the specified
+     * cause.
+     *
+     * @param cause the cause, may be {@code null}.
+     */
+    public BootstrapMethodError(Throwable cause) {
+        // cf. Throwable(Throwable cause) constructor.
+        super(cause == null ? null : cause.toString());
+        initCause(cause);
+    }
+}
diff --git a/ojluni/src/main/java/java/lang/Character.java b/ojluni/src/main/java/java/lang/Character.java
index a024b17..fb0d576 100644
--- a/ojluni/src/main/java/java/lang/Character.java
+++ b/ojluni/src/main/java/java/lang/Character.java
@@ -25,6 +25,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Locale;
@@ -5475,6 +5476,7 @@
         return isLowerCaseImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isLowerCaseImpl(int codePoint);
 
     /**
@@ -5542,6 +5544,7 @@
         return isUpperCaseImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isUpperCaseImpl(int codePoint);
 
 
@@ -5622,6 +5625,7 @@
         return isTitleCaseImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isTitleCaseImpl(int codePoint);
 
     /**
@@ -5697,6 +5701,7 @@
         return isDigitImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isDigitImpl(int codePoint);
 
     /**
@@ -5752,6 +5757,7 @@
         return isDefinedImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isDefinedImpl(int codePoint);
 
     /**
@@ -5826,6 +5832,7 @@
         return isLetterImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isLetterImpl(int codePoint);
 
     /**
@@ -5877,6 +5884,7 @@
         return isLetterOrDigitImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isLetterOrDigitImpl(int codePoint);
 
     /**
@@ -5971,6 +5979,7 @@
         return isAlphabeticImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isAlphabeticImpl(int codePoint);
 
 
@@ -5987,6 +5996,7 @@
     public static boolean isIdeographic(int codePoint) {
         return isIdeographicImpl(codePoint);
     }
+    @FastNative
     static native boolean isIdeographicImpl(int codePoint);
 
     /**
@@ -6206,6 +6216,7 @@
         return isUnicodeIdentifierStartImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isUnicodeIdentifierStartImpl(int codePoint);
 
     /**
@@ -6272,6 +6283,7 @@
         return isUnicodeIdentifierPartImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isUnicodeIdentifierPartImpl(int codePoint);
 
     /**
@@ -6339,6 +6351,7 @@
         return isIdentifierIgnorableImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isIdentifierIgnorableImpl(int codePoint);
 
     /**
@@ -6410,6 +6423,7 @@
         return toLowerCaseImpl(codePoint);
     }
 
+    @FastNative
     static native int toLowerCaseImpl(int codePoint);
 
     /**
@@ -6481,6 +6495,7 @@
         return toUpperCaseImpl(codePoint);
     }
 
+    @FastNative
     static native int toUpperCaseImpl(int codePoint);
 
     /**
@@ -6542,6 +6557,7 @@
         return toTitleCaseImpl(codePoint);
     }
 
+    @FastNative
     static native int toTitleCaseImpl(int codePoint);
 
     /**
@@ -6665,6 +6681,7 @@
         return digitImpl(codePoint, radix);
     }
 
+    @FastNative
     native static int digitImpl(int codePoint, int radix);
 
     /**
@@ -6759,6 +6776,7 @@
         return getNumericValueImpl(codePoint);
     }
 
+    @FastNative
     native static int getNumericValueImpl(int codePoint);
 
     /**
@@ -6867,6 +6885,7 @@
         return isSpaceCharImpl(codePoint);
     }
 
+    @FastNative
     static native boolean isSpaceCharImpl(int codePoint);
 
     /**
@@ -6961,6 +6980,7 @@
         return isWhitespaceImpl(codePoint);
     }
 
+    @FastNative
     native static boolean isWhitespaceImpl(int codePoint);
 
     /**
@@ -7103,6 +7123,7 @@
         return (type + 1);
     }
 
+    @FastNative
     static native int getTypeImpl(int codePoint);
 
     /**
@@ -7228,6 +7249,7 @@
         return Character.DIRECTIONALITY_UNDEFINED;
     }
 
+    @FastNative
     native static byte getDirectionalityImpl(int codePoint);
     /**
      * Determines whether the character is mirrored according to the
@@ -7271,6 +7293,7 @@
         return isMirroredImpl(codePoint);
     }
 
+    @FastNative
     native static boolean isMirroredImpl(int codePoint);
     /**
      * Compares two {@code Character} objects numerically.
diff --git a/ojluni/src/main/java/java/lang/Class.java b/ojluni/src/main/java/java/lang/Class.java
index 839b011..debee32 100644
--- a/ojluni/src/main/java/java/lang/Class.java
+++ b/ojluni/src/main/java/java/lang/Class.java
@@ -26,6 +26,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import com.android.dex.Dex;
 
 import java.io.InputStream;
@@ -462,6 +463,7 @@
     }
 
     /** Called after security checks have been made. */
+    @FastNative
     static native Class<?> classForName(String className, boolean shouldInitialize,
             ClassLoader classLoader) throws ClassNotFoundException;
 
@@ -500,6 +502,7 @@
      *          s.checkPackageAccess()} denies access to the package
      *          of this class.
      */
+    @FastNative
     public native T newInstance() throws InstantiationException, IllegalAccessException;
 
     /**
@@ -743,6 +746,7 @@
         return name;
     }
 
+    @FastNative
     private native String getNameNative();
 
     /**
@@ -972,6 +976,7 @@
     }
 
     // Returns the interfaces that this proxy class directly implements.
+    @FastNative
     private native Class<?>[] getProxyInterfaces();
 
     /**
@@ -1115,6 +1120,7 @@
         return null;
     }
 
+    @FastNative
     private native Method getEnclosingMethodNative();
 
     /**
@@ -1162,6 +1168,7 @@
         return getEnclosingConstructorNative();
     }
 
+    @FastNative
     private native Constructor<?> getEnclosingConstructorNative();
 
     private boolean classNameImpliesTopLevel() {
@@ -1181,6 +1188,7 @@
      * @since JDK1.1
      */
     // ANDROID-CHANGED: Removed SecurityException
+    @FastNative
     public native Class<?> getDeclaringClass();
 
     /**
@@ -1191,6 +1199,7 @@
      * @since 1.5
      */
     // ANDROID-CHANGED: Removed SecurityException
+    @FastNative
     public native Class<?> getEnclosingClass();
 
     /**
@@ -1295,6 +1304,7 @@
      * @return {@code true} if and only if this class is an anonymous class.
      * @since 1.5
      */
+    @FastNative
     public native boolean isAnonymousClass();
 
     /**
@@ -1606,6 +1616,7 @@
      *            if name is null.
      * @see #getField(String)
      */
+    @FastNative
     private native Field getPublicFieldRecursive(String name);
 
     /**
@@ -1757,6 +1768,7 @@
      * @since JDK1.1
      */
     // ANDROID-CHANGED: Removed SecurityException
+    @FastNative
     public native Class<?>[] getDeclaredClasses();
 
     /**
@@ -1802,6 +1814,7 @@
      * @jls 8.3 Field Declarations
      */
     // ANDROID-CHANGED: Removed SecurityException
+    @FastNative
     public native Field[] getDeclaredFields();
 
     /**
@@ -1811,6 +1824,7 @@
      * @param publicOnly Whether to return only public fields.
      * @hide
      */
+    @FastNative
     public native Field[] getDeclaredFieldsUnchecked(boolean publicOnly);
 
     /**
@@ -1882,6 +1896,7 @@
      * @param publicOnly Whether to return only public methods.
      * @hide
      */
+    @FastNative
     public native Method[] getDeclaredMethodsUnchecked(boolean publicOnly);
 
     /**
@@ -1931,6 +1946,7 @@
      * Returns the constructor with the given parameters if it is defined by this class;
      * {@code null} otherwise. This may return a non-public member.
      */
+    @FastNative
     private native Constructor<?>[] getDeclaredConstructorsInternal(boolean publicOnly);
 
     /**
@@ -1974,11 +1990,13 @@
      * @jls 8.3 Field Declarations
      */
     // ANDROID-CHANGED: Removed SecurityException
+    @FastNative
     public native Field getDeclaredField(String name) throws NoSuchFieldException;
 
     /**
      * Returns the subset of getDeclaredFields which are public.
      */
+    @FastNative
     private native Field[] getPublicDeclaredFields();
 
     /**
@@ -2320,6 +2338,7 @@
      *
      * @param args the types of the parameters to the constructor.
      */
+    @FastNative
     private native Constructor<T> getDeclaredConstructorInternal(Class<?>[] args);
 
     /**
@@ -2353,8 +2372,10 @@
     /**
      * Returns the simple name of a member or local class, or {@code null} otherwise.
      */
+    @FastNative
     private native String getInnerClassName();
 
+    @FastNative
     private native int getInnerClassFlags(int defaultValue);
 
     /**
@@ -2575,17 +2596,20 @@
      * @since 1.8
      */
     @Override
+    @FastNative
     public native <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass);
 
     /**
      * @since 1.5
      */
     @Override
+    @FastNative
     public native Annotation[] getDeclaredAnnotations();
 
     /**
      * Returns true if the annotation exists.
      */
+    @FastNative
     private native boolean isDeclaredAnnotationPresent(Class<? extends Annotation> annotationClass);
 
     private String getSignatureAttribute() {
@@ -2600,6 +2624,7 @@
         return result.toString();
     }
 
+    @FastNative
     private native String[] getSignatureAnnotation();
 
     /**
@@ -2752,6 +2777,7 @@
      * @param name the method name
      * @param args the method's parameter types
      */
+    @FastNative
     private native Method getDeclaredMethodInternal(String name, Class<?>[] args);
 
     /**
diff --git a/ojluni/src/main/java/java/lang/Math.java b/ojluni/src/main/java/java/lang/Math.java
index b53d35c..3ce39d9 100644
--- a/ojluni/src/main/java/java/lang/Math.java
+++ b/ojluni/src/main/java/java/lang/Math.java
@@ -25,6 +25,7 @@
  */
 
 package java.lang;
+import dalvik.annotation.optimization.FastNative;
 import java.util.Random;
 
 import sun.misc.FloatConsts;
@@ -136,6 +137,7 @@
      * @param   a   an angle, in radians.
      * @return  the sine of the argument.
      */
+    @FastNative
     public static native double sin(double a);
 
     /**
@@ -149,6 +151,7 @@
      * @param   a   an angle, in radians.
      * @return  the cosine of the argument.
      */
+    @FastNative
     public static native double cos(double a);
 
     /**
@@ -164,6 +167,7 @@
      * @param   a   an angle, in radians.
      * @return  the tangent of the argument.
      */
+    @FastNative
     public static native double tan(double a);
 
     /**
@@ -180,6 +184,7 @@
      * @param   a   the value whose arc sine is to be returned.
      * @return  the arc sine of the argument.
      */
+    @FastNative
     public static native double asin(double a);
 
     /**
@@ -194,6 +199,7 @@
      * @param   a   the value whose arc cosine is to be returned.
      * @return  the arc cosine of the argument.
      */
+    @FastNative
     public static native double acos(double a);
 
     /**
@@ -209,6 +215,7 @@
      * @param   a   the value whose arc tangent is to be returned.
      * @return  the arc tangent of the argument.
      */
+    @FastNative
     public static native double atan(double a);
 
     /**
@@ -257,6 +264,7 @@
      * @return  the value <i>e</i><sup>{@code a}</sup>,
      *          where <i>e</i> is the base of the natural logarithms.
      */
+    @FastNative
     public static native double exp(double a);
 
     /**
@@ -276,6 +284,7 @@
      * @return  the value ln&nbsp;{@code a}, the natural logarithm of
      *          {@code a}.
      */
+    @FastNative
     public static native double log(double a);
 
     /**
@@ -299,6 +308,7 @@
      * @return  the base 10 logarithm of  {@code a}.
      * @since 1.5
      */
+    @FastNative
     public static native double log10(double a);
 
     /**
@@ -318,6 +328,7 @@
      * @return  the positive square root of {@code a}.
      *          If the argument is NaN or less than zero, the result is NaN.
      */
+    @FastNative
     public static native double sqrt(double a);
 
 
@@ -347,6 +358,7 @@
      * @return  the cube root of {@code a}.
      * @since 1.5
      */
+    @FastNative
     public static native double cbrt(double a);
 
     /**
@@ -371,6 +383,7 @@
      * @return  the remainder when {@code f1} is divided by
      *          {@code f2}.
      */
+    @FastNative
     public static native double IEEEremainder(double f1, double f2);
 
     /**
@@ -392,6 +405,7 @@
      *          floating-point value that is greater than or equal to
      *          the argument and is equal to a mathematical integer.
      */
+    @FastNative
     public static native double ceil(double a);
 
     /**
@@ -409,6 +423,7 @@
      *          floating-point value that less than or equal to the argument
      *          and is equal to a mathematical integer.
      */
+    @FastNative
     public static native double floor(double a);
 
     /**
@@ -426,6 +441,7 @@
      * @return  the closest floating-point value to {@code a} that is
      *          equal to a mathematical integer.
      */
+    @FastNative
     public static native double rint(double a);
 
     /**
@@ -480,6 +496,7 @@
      *          in polar coordinates that corresponds to the point
      *          (<i>x</i>,&nbsp;<i>y</i>) in Cartesian coordinates.
      */
+    @FastNative
     public static native double atan2(double y, double x);
 
     /**
@@ -605,6 +622,7 @@
      * @param   b   the exponent.
      * @return  the value {@code a}<sup>{@code b}</sup>.
      */
+    @FastNative
     public static native double pow(double a, double b);
 
     /**
@@ -1564,6 +1582,7 @@
      * @return  The hyperbolic sine of {@code x}.
      * @since 1.5
      */
+    @FastNative
     public static native double sinh(double x);
 
     /**
@@ -1590,6 +1609,7 @@
      * @return  The hyperbolic cosine of {@code x}.
      * @since 1.5
      */
+    @FastNative
     public static native double cosh(double x);
 
     /**
@@ -1628,6 +1648,7 @@
      * @return  The hyperbolic tangent of {@code x}.
      * @since 1.5
      */
+    @FastNative
     public static native double tanh(double x);
 
     /**
@@ -1655,6 +1676,7 @@
      * without intermediate overflow or underflow
      * @since 1.5
      */
+    @FastNative
     public static native double hypot(double x, double y);
 
     /**
@@ -1691,6 +1713,7 @@
      * @return  the value <i>e</i><sup>{@code x}</sup>&nbsp;-&nbsp;1.
      * @since 1.5
      */
+    @FastNative
     public static native double expm1(double x);
 
     /**
@@ -1726,6 +1749,7 @@
      * log of {@code x}&nbsp;+&nbsp;1
      * @since 1.5
      */
+    @FastNative
     public static native double log1p(double x);
 
     /**
diff --git a/ojluni/src/main/java/java/lang/Object.java b/ojluni/src/main/java/java/lang/Object.java
index 811930f..e9728e0 100644
--- a/ojluni/src/main/java/java/lang/Object.java
+++ b/ojluni/src/main/java/java/lang/Object.java
@@ -26,6 +26,8 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * Class {@code Object} is the root of the class hierarchy.
  * Every class has {@code Object} as a superclass. All objects,
@@ -99,16 +101,27 @@
      * @see     java.lang.System#identityHashCode
      */
     public int hashCode() {
-        int lockWord = shadow$_monitor_;
+        return identityHashCode(this);
+    }
+
+    // Android-changed: add a local helper for identityHashCode.
+    // Package-private to be used by j.l.System. We do the implementation here
+    // to avoid Object.hashCode doing a clinit check on j.l.System, and also
+    // to avoid leaking shadow$_monitor_ outside of this class.
+    /* package-private */ static int identityHashCode(Object obj) {
+        int lockWord = obj.shadow$_monitor_;
         final int lockWordStateMask = 0xC0000000;  // Top 2 bits.
         final int lockWordStateHash = 0x80000000;  // Top 2 bits are value 2 (kStateHash).
         final int lockWordHashMask = 0x0FFFFFFF;  // Low 28 bits.
         if ((lockWord & lockWordStateMask) == lockWordStateHash) {
             return lockWord & lockWordHashMask;
         }
-        return System.identityHashCode(this);
+        return identityHashCodeNative(obj);
     }
 
+    @FastNative
+    private static native int identityHashCodeNative(Object obj);
+
     /**
      * Indicates whether some other object is "equal to" this one.
      * <p>
@@ -231,6 +244,7 @@
     /*
      * Native helper method for cloning.
      */
+    @FastNative
     private native Object internalClone();
 
 
@@ -291,6 +305,7 @@
      * @see        java.lang.Object#notifyAll()
      * @see        java.lang.Object#wait()
      */
+    @FastNative
     public final native void notify();
 
     /**
@@ -315,6 +330,7 @@
      * @see        java.lang.Object#notify()
      * @see        java.lang.Object#wait()
      */
+    @FastNative
     public final native void notifyAll();
 
     /**
@@ -468,6 +484,7 @@
      *             status</i> of the current thread is cleared when
      *             this exception is thrown.
      */
+    @FastNative
     public final native void wait(long millis, int nanos) throws InterruptedException;
 
     /**
@@ -508,6 +525,7 @@
      * @see        java.lang.Object#notify()
      * @see        java.lang.Object#notifyAll()
      */
+    @FastNative
     public final native void wait() throws InterruptedException;
 
     /**
diff --git a/ojluni/src/main/java/java/lang/Runtime.java b/ojluni/src/main/java/java/lang/Runtime.java
index 5a02009..3d52814 100644
--- a/ojluni/src/main/java/java/lang/Runtime.java
+++ b/ojluni/src/main/java/java/lang/Runtime.java
@@ -26,6 +26,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import java.io.*;
 import java.util.StringTokenizer;
 import sun.reflect.CallerSensitive;
@@ -719,6 +720,7 @@
      * @return  an approximation to the total amount of memory currently
      *          available for future allocated objects, measured in bytes.
      */
+    @FastNative
     public native long freeMemory();
 
     /**
@@ -732,6 +734,7 @@
      * @return  the total amount of memory currently available for current
      *          and future objects, measured in bytes.
      */
+    @FastNative
     public native long totalMemory();
 
     /**
@@ -743,6 +746,7 @@
      *          attempt to use, measured in bytes
      * @since 1.4
      */
+    @FastNative
     public native long maxMemory();
 
     /**
diff --git a/ojluni/src/main/java/java/lang/StackTraceElement.java b/ojluni/src/main/java/java/lang/StackTraceElement.java
index 58738d8..e519fea 100644
--- a/ojluni/src/main/java/java/lang/StackTraceElement.java
+++ b/ojluni/src/main/java/java/lang/StackTraceElement.java
@@ -169,11 +169,27 @@
      * @see    Throwable#printStackTrace()
      */
     public String toString() {
-        return getClassName() + "." + methodName +
-            (isNativeMethod() ? "(Native Method)" :
-             (fileName != null && lineNumber >= 0 ?
-              "(" + fileName + ":" + lineNumber + ")" :
-              (fileName != null ?  "("+fileName+")" : "(Unknown Source)")));
+        // Android-changed: When ART cannot find a line number, the lineNumber field is set
+        // to the dex_pc and the fileName field is set to null.
+        StringBuilder result = new StringBuilder();
+        result.append(getClassName()).append(".").append(methodName);
+        if (isNativeMethod()) {
+            result.append("(Native Method)");
+        } else if (fileName != null) {
+            if (lineNumber >= 0) {
+                result.append("(").append(fileName).append(":").append(lineNumber).append(")");
+            } else {
+                result.append("(").append(fileName).append(")");
+            }
+        } else {
+            if (lineNumber >= 0) {
+                // The line number is actually the dex pc.
+                result.append("(Unknown Source:").append(lineNumber).append(")");
+            } else {
+                result.append("(Unknown Source)");
+            }
+        }
+        return result.toString();
     }
 
     /**
diff --git a/ojluni/src/main/java/java/lang/String.java b/ojluni/src/main/java/java/lang/String.java
index 2d985ca..2b588a5 100644
--- a/ojluni/src/main/java/java/lang/String.java
+++ b/ojluni/src/main/java/java/lang/String.java
@@ -25,6 +25,7 @@
  */
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import java.io.ObjectStreamField;
 import java.io.UnsupportedEncodingException;
 import java.lang.ArrayIndexOutOfBoundsException;
@@ -536,7 +537,7 @@
      *          object.
      */
     public int length() {
-        final boolean STRING_COMPRESSION_ENABLED = false;
+        final boolean STRING_COMPRESSION_ENABLED = true;
         if (STRING_COMPRESSION_ENABLED) {
             // For the compression purposes (save the characters as 8-bit if all characters
             // are ASCII), the least significant bit of "count" is used as the compression flag.
@@ -577,10 +578,9 @@
      *             argument is negative or not less than the length of this
      *             string.
      */
+    @FastNative
     public native char charAt(int index);
 
-    native void setCharAt(int index, char c);
-
     /**
      * Returns the character (Unicode code point) at the specified
      * index. The index refers to {@code char} values
@@ -773,6 +773,7 @@
      * within the java.lang package only.  The caller is responsible for
      * ensuring that start >= 0 && start <= end && end <= count.
      */
+    @FastNative
     native void getCharsNoCheck(int start, int end, char[] buffer, int index);
 
 
@@ -1113,6 +1114,7 @@
      *          value greater than {@code 0} if this string is
      *          lexicographically greater than the string argument.
      */
+    @FastNative
     public native int compareTo(String anotherString);
 
     /**
@@ -1504,6 +1506,7 @@
         }
     }
 
+    @FastNative
     private native int fastIndexOf(int c, int start);
 
     /**
@@ -1976,6 +1979,7 @@
                 : fastSubstring(beginIndex, subLen);
     }
 
+    @FastNative
     private native String fastSubstring(int start, int length);
 
     /**
@@ -2031,6 +2035,7 @@
      * @return  a string that represents the concatenation of this object's
      *          characters followed by the string argument's characters.
      */
+    @FastNative
     public native String concat(String str);
 
     /**
@@ -2063,21 +2068,21 @@
      *          occurrence of {@code oldChar} with {@code newChar}.
      */
     public String replace(char oldChar, char newChar) {
-        String replaced = this;
         if (oldChar != newChar) {
             final int len = length();
             for (int i = 0; i < len; ++i) {
                 if (charAt(i) == oldChar) {
-                    if (replaced == this) {
-                        replaced = StringFactory.newStringFromString(this);
-                    }
-                    replaced.setCharAt(i, newChar);
+                    return doReplace(oldChar, newChar);
                 }
             }
         }
-        return replaced;
+        return this;
     }
 
+    // Implementation of replace(char oldChar, char newChar) called when we found a match.
+    @FastNative
+    private native String doReplace(char oldChar, char newChar);
+
     /**
      * Tells whether or not this string matches the given <a
      * href="../util/regex/Pattern.html#sum">regular expression</a>.
@@ -2719,6 +2724,7 @@
      *          of this string and whose contents are initialized to contain
      *          the character sequence represented by this string.
      */
+    @FastNative
     public native char[] toCharArray();
 
 
@@ -2991,5 +2997,6 @@
      * @return  a string that has the same contents as this string, but is
      *          guaranteed to be from a pool of unique strings.
      */
+    @FastNative
     public native String intern();
 }
diff --git a/ojluni/src/main/java/java/lang/System.java b/ojluni/src/main/java/java/lang/System.java
index 04f4465..03fa221 100644
--- a/ojluni/src/main/java/java/lang/System.java
+++ b/ojluni/src/main/java/java/lang/System.java
@@ -25,6 +25,7 @@
  */
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import android.system.ErrnoException;
 import android.system.StructPasswd;
 import android.system.StructUtsname;
@@ -433,6 +434,7 @@
      * @exception  NullPointerException if either <code>src</code> or
      *               <code>dest</code> is <code>null</code>.
      */
+    @FastNative
     public static native void arraycopy(Object src,  int  srcPos,
                                         Object dest, int destPos,
                                         int length);
@@ -489,6 +491,7 @@
      * The char[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyCharUnchecked(char[] src, int srcPos,
         char[] dst, int dstPos, int length);
 
@@ -542,6 +545,7 @@
      * The byte[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyByteUnchecked(byte[] src, int srcPos,
         byte[] dst, int dstPos, int length);
 
@@ -595,6 +599,7 @@
      * The short[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyShortUnchecked(short[] src, int srcPos,
         short[] dst, int dstPos, int length);
 
@@ -648,6 +653,7 @@
      * The int[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyIntUnchecked(int[] src, int srcPos,
         int[] dst, int dstPos, int length);
 
@@ -701,6 +707,7 @@
      * The long[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyLongUnchecked(long[] src, int srcPos,
         long[] dst, int dstPos, int length);
 
@@ -754,6 +761,7 @@
      * The float[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyFloatUnchecked(float[] src, int srcPos,
         float[] dst, int dstPos, int length);
 
@@ -807,6 +815,7 @@
      * The double[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyDoubleUnchecked(double[] src, int srcPos,
         double[] dst, int dstPos, int length);
 
@@ -860,6 +869,7 @@
      * The boolean[] specialized, unchecked, native version of
      * arraycopy(). This assumes error checking has been done.
      */
+    @FastNative
     private static native void arraycopyBooleanUnchecked(boolean[] src, int srcPos,
         boolean[] dst, int dstPos, int length);
     // ----- END android -----
@@ -875,7 +885,12 @@
      * @return  the hashCode
      * @since   JDK1.1
      */
-    public static native int identityHashCode(Object x);
+    public static int identityHashCode(Object x) {
+        if (x == null) {
+            return 0;
+        }
+        return Object.identityHashCode(x);
+    }
 
     /**
      * System properties. The following properties are guaranteed to be defined:
diff --git a/ojluni/src/main/java/java/lang/Thread.java b/ojluni/src/main/java/java/lang/Thread.java
index 27c820e..18cff06 100644
--- a/ojluni/src/main/java/java/lang/Thread.java
+++ b/ojluni/src/main/java/java/lang/Thread.java
@@ -26,6 +26,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.FastNative;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
@@ -271,6 +272,7 @@
      *
      * @return  the currently executing thread.
      */
+    @FastNative
     public static native Thread currentThread();
 
     /**
@@ -312,6 +314,7 @@
         Thread.sleep(millis, 0);
     }
 
+    @FastNative
     private static native void sleep(Object lock, long millis, int nanos)
         throws InterruptedException;
 
@@ -942,6 +945,7 @@
      * @see #isInterrupted()
      * @revised 6.0
      */
+    @FastNative
     public static native boolean interrupted();
 
     /**
@@ -957,6 +961,7 @@
      * @see     #interrupted()
      * @revised 6.0
      */
+    @FastNative
     public native boolean isInterrupted();
 
     /**
@@ -2036,6 +2041,7 @@
 
     private native int nativeGetStatus(boolean hasBeenStarted);
 
+    @FastNative
     private native void nativeInterrupt();
 
     /** Park states */
diff --git a/ojluni/src/main/java/java/lang/Throwable.java b/ojluni/src/main/java/java/lang/Throwable.java
index 86e2939..fe3c69d 100644
--- a/ojluni/src/main/java/java/lang/Throwable.java
+++ b/ojluni/src/main/java/java/lang/Throwable.java
@@ -25,6 +25,7 @@
  */
 
 package java.lang;
+import dalvik.annotation.optimization.FastNative;
 import  java.io.*;
 import  java.util.*;
 
@@ -777,6 +778,7 @@
         return this;
     }
 
+    @FastNative
     private static native Object nativeFillInStackTrace();
 
     /**
@@ -881,6 +883,7 @@
      * @throws IndexOutOfBoundsException if {@code index < 0 ||
      *         index >= getStackTraceDepth() }
      */
+    @FastNative
     private static native StackTraceElement[] nativeGetStackTrace(Object stackState);
 
 
diff --git a/ojluni/src/main/java/java/lang/Void.java b/ojluni/src/main/java/java/lang/Void.java
index 91778b1..8311ea8 100644
--- a/ojluni/src/main/java/java/lang/Void.java
+++ b/ojluni/src/main/java/java/lang/Void.java
@@ -46,6 +46,12 @@
      */
     public static final Class<Void> TYPE = lookupType();
 
+    // Android-changed: Upstream code would use reflection to establish the value of "void.class".
+    // ART makes a native call instead because the reflection approach could lead to initialization
+    // of TYPE with the current, i.e. uninitialized, value of TYPE due to other Android changes.
+    @dalvik.annotation.optimization.FastNative
+    private static native Class<Void> lookupType();
+    /*
     @SuppressWarnings("unchecked")
     private static Class<Void> lookupType() {
         try {
@@ -55,6 +61,7 @@
             throw new AssertionError(e);
         }
     }
+    */
 
     /*
      * The Void class cannot be instantiated.
diff --git a/ojluni/src/main/java/java/lang/invoke/CallSite.java b/ojluni/src/main/java/java/lang/invoke/CallSite.java
new file mode 100644
index 0000000..f9338e6
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/CallSite.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: Not using Empty
+//import sun.invoke.empty.Empty;
+import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+
+/**
+ * A {@code CallSite} is a holder for a variable {@link MethodHandle},
+ * which is called its {@code target}.
+ * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
+ * all calls to the site's current target.
+ * A {@code CallSite} may be associated with several {@code invokedynamic}
+ * instructions, or it may be "free floating", associated with none.
+ * In any case, it may be invoked through an associated method handle
+ * called its {@linkplain #dynamicInvoker dynamic invoker}.
+ * <p>
+ * {@code CallSite} is an abstract class which does not allow
+ * direct subclassing by users.  It has three immediate,
+ * concrete subclasses that may be either instantiated or subclassed.
+ * <ul>
+ * <li>If a mutable target is not required, an {@code invokedynamic} instruction
+ * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
+ * <li>If a mutable target is required which has volatile variable semantics,
+ * because updates to the target must be immediately and reliably witnessed by other threads,
+ * a {@linkplain VolatileCallSite volatile call site} may be used.
+ * <li>Otherwise, if a mutable target is required,
+ * a {@linkplain MutableCallSite mutable call site} may be used.
+ * </ul>
+ * <p>
+ * A non-constant call site may be <em>relinked</em> by changing its target.
+ * The new target must have the same {@linkplain MethodHandle#type() type}
+ * as the previous target.
+ * Thus, though a call site can be relinked to a series of
+ * successive targets, it cannot change its type.
+ * <p>
+ * Here is a sample use of call sites and bootstrap methods which links every
+ * dynamic call site to print its arguments:
+<blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java -->
+static void test() throws Throwable {
+    // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
+    InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
+}
+private static void printArgs(Object... args) {
+  System.out.println(java.util.Arrays.deepToString(args));
+}
+private static final MethodHandle printArgs;
+static {
+  MethodHandles.Lookup lookup = MethodHandles.lookup();
+  Class thisClass = lookup.lookupClass();  // (who am I?)
+  printArgs = lookup.findStatic(thisClass,
+      "printArgs", MethodType.methodType(void.class, Object[].class));
+}
+private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
+  // ignore caller and name, but match the type:
+  return new ConstantCallSite(printArgs.asType(type));
+}
+</pre></blockquote>
+ * @author John Rose, JSR 292 EG
+ */
+abstract
+public class CallSite {
+    // Android-changed: not used.
+    // static { MethodHandleImpl.initStatics(); }
+
+    // The actual payload of this call site:
+    /*package-private*/
+    MethodHandle target;    // Note: This field is known to the JVM.  Do not change.
+
+    /**
+     * Make a blank call site object with the given method type.
+     * An initial target method is supplied which will throw
+     * an {@link IllegalStateException} if called.
+     * <p>
+     * Before this {@code CallSite} object is returned from a bootstrap method,
+     * it is usually provided with a more useful target method,
+     * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+     * @throws NullPointerException if the proposed type is null
+     */
+
+    /*package-private*/
+    CallSite(MethodType type) {
+        // Android-changed: No cache for these so create uninitializedCallSite target here using
+        // method handle transformations to create a method handle that has the expected method
+        // type but throws an IllegalStateException.
+        // target = type.invokers().uninitializedCallSite();
+        this.target = MethodHandles.throwException(type.returnType(), IllegalStateException.class);
+        this.target = MethodHandles.insertArguments(
+            this.target, 0, new IllegalStateException("uninitialized call site"));
+        if (type.parameterCount() > 0) {
+            this.target = MethodHandles.dropArguments(this.target, 0, type.ptypes());
+        }
+
+        // Android-changed: Using initializer method for GET_TARGET
+        // rather than complex static initializer.
+        initializeGetTarget();
+    }
+
+    /**
+     * Make a call site object equipped with an initial target method handle.
+     * @param target the method handle which will be the initial target of the call site
+     * @throws NullPointerException if the proposed target is null
+     */
+    /*package-private*/
+    CallSite(MethodHandle target) {
+        target.type();  // null check
+        this.target = target;
+
+        // Android-changed: Using initializer method for GET_TARGET
+        // rather than complex static initializer.
+        initializeGetTarget();
+    }
+
+    /**
+     * Make a call site object equipped with an initial target method handle.
+     * @param targetType the desired type of the call site
+     * @param createTargetHook a hook which will bind the call site to the target method handle
+     * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
+     *         or if the target returned by the hook is not of the given {@code targetType}
+     * @throws NullPointerException if the hook returns a null value
+     * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
+     * @throws Throwable anything else thrown by the the hook function
+     */
+    /*package-private*/
+    CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
+        this(targetType);
+        ConstantCallSite selfCCS = (ConstantCallSite) this;
+        MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
+        checkTargetChange(this.target, boundTarget);
+        this.target = boundTarget;
+
+        // Android-changed: Using initializer method for GET_TARGET
+        // rather than complex static initializer.
+        initializeGetTarget();
+    }
+
+    /**
+     * Returns the type of this call site's target.
+     * Although targets may change, any call site's type is permanent, and can never change to an unequal type.
+     * The {@code setTarget} method enforces this invariant by refusing any new target that does
+     * not have the previous target's type.
+     * @return the type of the current target, which is also the type of any future target
+     */
+    public MethodType type() {
+        // warning:  do not call getTarget here, because CCS.getTarget can throw IllegalStateException
+        return target.type();
+    }
+
+    /**
+     * Returns the target method of the call site, according to the
+     * behavior defined by this call site's specific class.
+     * The immediate subclasses of {@code CallSite} document the
+     * class-specific behaviors of this method.
+     *
+     * @return the current linkage state of the call site, its target method handle
+     * @see ConstantCallSite
+     * @see VolatileCallSite
+     * @see #setTarget
+     * @see ConstantCallSite#getTarget
+     * @see MutableCallSite#getTarget
+     * @see VolatileCallSite#getTarget
+     */
+    public abstract MethodHandle getTarget();
+
+    /**
+     * Updates the target method of this call site, according to the
+     * behavior defined by this call site's specific class.
+     * The immediate subclasses of {@code CallSite} document the
+     * class-specific behaviors of this method.
+     * <p>
+     * The type of the new target must be {@linkplain MethodType#equals equal to}
+     * the type of the old target.
+     *
+     * @param newTarget the new target
+     * @throws NullPointerException if the proposed new target is null
+     * @throws WrongMethodTypeException if the proposed new target
+     *         has a method type that differs from the previous target
+     * @see CallSite#getTarget
+     * @see ConstantCallSite#setTarget
+     * @see MutableCallSite#setTarget
+     * @see VolatileCallSite#setTarget
+     */
+    public abstract void setTarget(MethodHandle newTarget);
+
+    void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
+        MethodType oldType = oldTarget.type();
+        MethodType newType = newTarget.type();  // null check!
+        if (!newType.equals(oldType))
+            throw wrongTargetType(newTarget, oldType);
+    }
+
+    private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
+        return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
+    }
+
+    /**
+     * Produces a method handle equivalent to an invokedynamic instruction
+     * which has been linked to this call site.
+     * <p>
+     * This method is equivalent to the following code:
+     * <blockquote><pre>
+     * MethodHandle getTarget, invoker, result;
+     * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
+     * invoker = MethodHandles.exactInvoker(this.type());
+     * result = MethodHandles.foldArguments(invoker, getTarget)
+     * </pre></blockquote>
+     *
+     * @return a method handle which always invokes this call site's current target
+     */
+    public abstract MethodHandle dynamicInvoker();
+
+    /*non-public*/ MethodHandle makeDynamicInvoker() {
+        // Android-changed: Use bindTo() rather than bindReceiver() (not implemented).
+        MethodHandle getTarget = GET_TARGET.bindTo(this);
+        MethodHandle invoker = MethodHandles.exactInvoker(this.type());
+        return MethodHandles.foldArguments(invoker, getTarget);
+    }
+
+    // Android-changed: no longer final. GET_TARGET assigned in initializeGetTarget().
+    private static MethodHandle GET_TARGET = null;
+
+    private void initializeGetTarget() {
+        // Android-changed: moved from static initializer for
+        // GET_TARGET to avoid issues with running early. Called from
+        // constructors. CallSite creation is not performance critical.
+        synchronized (CallSite.class) {
+            if (GET_TARGET == null) {
+                try {
+                    GET_TARGET = IMPL_LOOKUP.
+                            findVirtual(CallSite.class, "getTarget",
+                                        MethodType.methodType(MethodHandle.class));
+                } catch (ReflectiveOperationException e) {
+                    throw new InternalError(e);
+                }
+            }
+        }
+    }
+
+    /* Android-changed: not used. */
+    // /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
+    // /*package-private*/
+    // static Empty uninitializedCallSite() {
+    //     throw new IllegalStateException("uninitialized call site");
+    // }
+
+    // unsafe stuff:
+    private static final long TARGET_OFFSET;
+    static {
+        try {
+            TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
+        } catch (Exception ex) { throw new Error(ex); }
+    }
+
+    /*package-private*/
+    void setTargetNormal(MethodHandle newTarget) {
+        // Android-changed: Set value directly.
+        // MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
+        target = newTarget;
+    }
+    /*package-private*/
+    MethodHandle getTargetVolatile() {
+        return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
+    }
+    /*package-private*/
+    void setTargetVolatile(MethodHandle newTarget) {
+        // Android-changed: Set value directly.
+        // MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
+        UNSAFE.putObjectVolatile(this, TARGET_OFFSET, newTarget);
+    }
+
+    /* Android-changed: not used. */
+    // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
+    // static CallSite makeSite(MethodHandle bootstrapMethod,
+    //                          // Callee information:
+    //                          String name, MethodType type,
+    //                          // Extra arguments for BSM, if any:
+    //                          Object info,
+    //                          // Caller information:
+    //                          Class<?> callerClass) {
+    //     Object caller = IMPL_LOOKUP.in(callerClass);
+    //     CallSite site;
+    //     try {
+    //         Object binding;
+    //         info = maybeReBox(info);
+    //         if (info == null) {
+    //             binding = bootstrapMethod.invoke(caller, name, type);
+    //         } else if (!info.getClass().isArray()) {
+    //             binding = bootstrapMethod.invoke(caller, name, type, info);
+    //         } else {
+    //             Object[] argv = (Object[]) info;
+    //             maybeReBoxElements(argv);
+    //             if (3 + argv.length > 255)
+    //                 throw new BootstrapMethodError("too many bootstrap method arguments");
+    //             MethodType bsmType = bootstrapMethod.type();
+    //             if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
+    //                 binding = bootstrapMethod.invoke(caller, name, type, argv);
+    //             else
+    //                 binding = MethodHandles.spreadInvoker(bsmType, 3)
+    //                     .invoke(bootstrapMethod, caller, name, type, argv);
+    //         }
+    //         //System.out.println("BSM for "+name+type+" => "+binding);
+    //         if (binding instanceof CallSite) {
+    //             site = (CallSite) binding;
+    //         }  else {
+    //             throw new ClassCastException("bootstrap method failed to produce a CallSite");
+    //         }
+    //         if (!site.getTarget().type().equals(type))
+    //             throw new WrongMethodTypeException("wrong type: "+site.getTarget());
+    //     } catch (Throwable ex) {
+    //         BootstrapMethodError bex;
+    //         if (ex instanceof BootstrapMethodError)
+    //             bex = (BootstrapMethodError) ex;
+    //         else
+    //             bex = new BootstrapMethodError("call site initialization exception", ex);
+    //         throw bex;
+    //     }
+    //     return site;
+    // }
+    // private static Object maybeReBox(Object x) {
+    //     if (x instanceof Integer) {
+    //         int xi = (int) x;
+    //         if (xi == (byte) xi)
+    //             x = xi;  // must rebox; see JLS 5.1.7
+    //     }
+    //     return x;
+    // }
+    // private static void maybeReBoxElements(Object[] xa) {
+    //     for (int i = 0; i < xa.length; i++) {
+    //         xa[i] = maybeReBox(xa[i]);
+    //     }
+    // }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java b/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java
new file mode 100644
index 0000000..2d9fede
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+/**
+ * A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
+ * An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently
+ * bound to the call site's target.
+ * @author John Rose, JSR 292 EG
+ */
+public class ConstantCallSite extends CallSite {
+    private final boolean isFrozen;
+
+    /**
+     * Creates a call site with a permanent target.
+     * @param target the target to be permanently associated with this call site
+     * @throws NullPointerException if the proposed target is null
+     */
+    public ConstantCallSite(MethodHandle target) {
+        super(target);
+        isFrozen = true;
+    }
+
+    /**
+     * Creates a call site with a permanent target, possibly bound to the call site itself.
+     * <p>
+     * During construction of the call site, the {@code createTargetHook} is invoked to
+     * produce the actual target, as if by a call of the form
+     * {@code (MethodHandle) createTargetHook.invoke(this)}.
+     * <p>
+     * Note that user code cannot perform such an action directly in a subclass constructor,
+     * since the target must be fixed before the {@code ConstantCallSite} constructor returns.
+     * <p>
+     * The hook is said to bind the call site to a target method handle,
+     * and a typical action would be {@code someTarget.bindTo(this)}.
+     * However, the hook is free to take any action whatever,
+     * including ignoring the call site and returning a constant target.
+     * <p>
+     * The result returned by the hook must be a method handle of exactly
+     * the same type as the call site.
+     * <p>
+     * While the hook is being called, the new {@code ConstantCallSite}
+     * object is in a partially constructed state.
+     * In this state,
+     * a call to {@code getTarget}, or any other attempt to use the target,
+     * will result in an {@code IllegalStateException}.
+     * It is legal at all times to obtain the call site's type using the {@code type} method.
+     *
+     * @param targetType the type of the method handle to be permanently associated with this call site
+     * @param createTargetHook a method handle to invoke (on the call site) to produce the call site's target
+     * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
+     *         or if the target returned by the hook is not of the given {@code targetType}
+     * @throws NullPointerException if the hook returns a null value
+     * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
+     * @throws Throwable anything else thrown by the the hook function
+     */
+    protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
+        super(targetType, createTargetHook);
+        isFrozen = true;
+    }
+
+    /**
+     * Returns the target method of the call site, which behaves
+     * like a {@code final} field of the {@code ConstantCallSite}.
+     * That is, the the target is always the original value passed
+     * to the constructor call which created this instance.
+     *
+     * @return the immutable linkage state of this call site, a constant method handle
+     * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
+     */
+    @Override public final MethodHandle getTarget() {
+        if (!isFrozen)  throw new IllegalStateException();
+        return target;
+    }
+
+    /**
+     * Always throws an {@link UnsupportedOperationException}.
+     * This kind of call site cannot change its target.
+     * @param ignore a new target proposed for the call site, which is ignored
+     * @throws UnsupportedOperationException because this kind of call site cannot change its target
+     */
+    @Override public final void setTarget(MethodHandle ignore) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns this call site's permanent target.
+     * Since that target will never change, this is a correct implementation
+     * of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}.
+     * @return the immutable linkage state of this call site, a constant method handle
+     * @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
+     */
+    @Override
+    public final MethodHandle dynamicInvoker() {
+        return getTarget();
+    }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
index c41e0c2..6e414d0 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
@@ -139,7 +139,7 @@
         MethodHandleImpl directTarget = getMethodHandleImpl(target);
         // Given that this is specified to be an "unchecked" crack, we can directly allocate
         // a member from the underlying ArtField / Method and bypass all associated access checks.
-        return (T) directTarget.getMemberInternal();
+        return expected.cast(directTarget.getMemberInternal());
     }
 
     /**
@@ -665,6 +665,9 @@
          */
         static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, PUBLIC);
 
+        /** Package-private version of lookup which is trusted. */
+        static final Lookup IMPL_LOOKUP = new Lookup(Object.class, ALL_MODES);
+
         private static void checkUnprivilegedlookupClass(Class<?> lookupClass, int allowedModes) {
             String name = lookupClass.getName();
             if (name.startsWith("java.lang.invoke."))
@@ -1126,6 +1129,10 @@
         private MethodHandle findSpecial(Method method, MethodType type,
                                          Class<?> refc, Class<?> specialCaller)
                 throws IllegalAccessException {
+            if (Modifier.isStatic(method.getModifiers())) {
+                throw new IllegalAccessException("expected a non-static method:" + method);
+            }
+
             if (Modifier.isPrivate(method.getModifiers())) {
                 // Since this is a private method, we'll need to also make sure that the
                 // lookup class is the same as the refering class. We've already checked that
@@ -1656,7 +1663,7 @@
             }
         }
 
-        public void throwMakeAccessException(String message, Object from) throws
+        private void throwMakeAccessException(String message, Object from) throws
                 IllegalAccessException{
             message = message + ": "+ toString();
             if (from != null)  message += ", from " + from;
diff --git a/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java b/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java
new file mode 100644
index 0000000..37ca324
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/MutableCallSite.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: not used.
+// import java.util.concurrent.atomic.AtomicInteger;
+
+// Android-changed: removed references to MutableCallSite.syncAll().
+/**
+ * A {@code MutableCallSite} is a {@link CallSite} whose target variable
+ * behaves like an ordinary field.
+ * An {@code invokedynamic} instruction linked to a {@code MutableCallSite} delegates
+ * all calls to the site's current target.
+ * The {@linkplain CallSite#dynamicInvoker dynamic invoker} of a mutable call site
+ * also delegates each call to the site's current target.
+ * <p>
+ * Here is an example of a mutable call site which introduces a
+ * state variable into a method handle chain.
+ * <!-- JavaDocExamplesTest.testMutableCallSite -->
+ * <blockquote><pre>
+MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
+MethodHandle MH_name = name.dynamicInvoker();
+MethodType MT_str1 = MethodType.methodType(String.class);
+MethodHandle MH_upcase = MethodHandles.lookup()
+    .findVirtual(String.class, "toUpperCase", MT_str1);
+MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
+name.setTarget(MethodHandles.constant(String.class, "Rocky"));
+assertEquals("ROCKY", (String) worker1.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Fred"));
+assertEquals("FRED", (String) worker1.invokeExact());
+// (mutation can be continued indefinitely)
+ * </pre></blockquote>
+ * <p>
+ * The same call site may be used in several places at once.
+ * <blockquote><pre>
+MethodType MT_str2 = MethodType.methodType(String.class, String.class);
+MethodHandle MH_cat = lookup().findVirtual(String.class,
+  "concat", methodType(String.class, String.class));
+MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
+MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
+assertEquals("Fred, dear?", (String) worker2.invokeExact());
+name.setTarget(MethodHandles.constant(String.class, "Wilma"));
+assertEquals("WILMA", (String) worker1.invokeExact());
+assertEquals("Wilma, dear?", (String) worker2.invokeExact());
+ * </pre></blockquote>
+ * <p>
+ * <em>Non-synchronization of target values:</em>
+ * A write to a mutable call site's target does not force other threads
+ * to become aware of the updated value.  Threads which do not perform
+ * suitable synchronization actions relative to the updated call site
+ * may cache the old target value and delay their use of the new target
+ * value indefinitely.
+ * (This is a normal consequence of the Java Memory Model as applied
+ * to object fields.)
+ * <p>
+ * For target values which will be frequently updated, consider using
+ * a {@linkplain VolatileCallSite volatile call site} instead.
+ * @author John Rose, JSR 292 EG
+ */
+public class MutableCallSite extends CallSite {
+    /**
+     * Creates a blank call site object with the given method type.
+     * The initial target is set to a method handle of the given type
+     * which will throw an {@link IllegalStateException} if called.
+     * <p>
+     * The type of the call site is permanently set to the given type.
+     * <p>
+     * Before this {@code CallSite} object is returned from a bootstrap method,
+     * or invoked in some other manner,
+     * it is usually provided with a more useful target method,
+     * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+     * @param type the method type that this call site will have
+     * @throws NullPointerException if the proposed type is null
+     */
+    public MutableCallSite(MethodType type) {
+        super(type);
+    }
+
+    /**
+     * Creates a call site object with an initial target method handle.
+     * The type of the call site is permanently set to the initial target's type.
+     * @param target the method handle that will be the initial target of the call site
+     * @throws NullPointerException if the proposed target is null
+     */
+    public MutableCallSite(MethodHandle target) {
+        super(target);
+    }
+
+    /**
+     * Returns the target method of the call site, which behaves
+     * like a normal field of the {@code MutableCallSite}.
+     * <p>
+     * The interactions of {@code getTarget} with memory are the same
+     * as of a read from an ordinary variable, such as an array element or a
+     * non-volatile, non-final field.
+     * <p>
+     * In particular, the current thread may choose to reuse the result
+     * of a previous read of the target from memory, and may fail to see
+     * a recent update to the target by another thread.
+     *
+     * @return the linkage state of this call site, a method handle which can change over time
+     * @see #setTarget
+     */
+    @Override public final MethodHandle getTarget() {
+        return target;
+    }
+
+    /**
+     * Updates the target method of this call site, as a normal variable.
+     * The type of the new target must agree with the type of the old target.
+     * <p>
+     * The interactions with memory are the same
+     * as of a write to an ordinary variable, such as an array element or a
+     * non-volatile, non-final field.
+     * <p>
+     * In particular, unrelated threads may fail to see the updated target
+     * until they perform a read from memory.
+     * Stronger guarantees can be created by putting appropriate operations
+     * into the bootstrap method and/or the target methods used
+     * at any given call site.
+     *
+     * @param newTarget the new target
+     * @throws NullPointerException if the proposed new target is null
+     * @throws WrongMethodTypeException if the proposed new target
+     *         has a method type that differs from the previous target
+     * @see #getTarget
+     */
+    @Override public void setTarget(MethodHandle newTarget) {
+        checkTargetChange(this.target, newTarget);
+        setTargetNormal(newTarget);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final MethodHandle dynamicInvoker() {
+        return makeDynamicInvoker();
+    }
+
+    // Android-changed: not exposing incomplete implementation.
+    // /**
+    //  * Performs a synchronization operation on each call site in the given array,
+    //  * forcing all other threads to throw away any cached values previously
+    //  * loaded from the target of any of the call sites.
+    //  * <p>
+    //  * This operation does not reverse any calls that have already started
+    //  * on an old target value.
+    //  * (Java supports {@linkplain java.lang.Object#wait() forward time travel} only.)
+    //  * <p>
+    //  * The overall effect is to force all future readers of each call site's target
+    //  * to accept the most recently stored value.
+    //  * ("Most recently" is reckoned relative to the {@code syncAll} itself.)
+    //  * Conversely, the {@code syncAll} call may block until all readers have
+    //  * (somehow) decached all previous versions of each call site's target.
+    //  * <p>
+    //  * To avoid race conditions, calls to {@code setTarget} and {@code syncAll}
+    //  * should generally be performed under some sort of mutual exclusion.
+    //  * Note that reader threads may observe an updated target as early
+    //  * as the {@code setTarget} call that install the value
+    //  * (and before the {@code syncAll} that confirms the value).
+    //  * On the other hand, reader threads may observe previous versions of
+    //  * the target until the {@code syncAll} call returns
+    //  * (and after the {@code setTarget} that attempts to convey the updated version).
+    //  * <p>
+    //  * This operation is likely to be expensive and should be used sparingly.
+    //  * If possible, it should be buffered for batch processing on sets of call sites.
+    //  * <p>
+    //  * If {@code sites} contains a null element,
+    //  * a {@code NullPointerException} will be raised.
+    //  * In this case, some non-null elements in the array may be
+    //  * processed before the method returns abnormally.
+    //  * Which elements these are (if any) is implementation-dependent.
+    //  *
+    //  * <h3>Java Memory Model details</h3>
+    //  * In terms of the Java Memory Model, this operation performs a synchronization
+    //  * action which is comparable in effect to the writing of a volatile variable
+    //  * by the current thread, and an eventual volatile read by every other thread
+    //  * that may access one of the affected call sites.
+    //  * <p>
+    //  * The following effects are apparent, for each individual call site {@code S}:
+    //  * <ul>
+    //  * <li>A new volatile variable {@code V} is created, and written by the current thread.
+    //  *     As defined by the JMM, this write is a global synchronization event.
+    //  * <li>As is normal with thread-local ordering of write events,
+    //  *     every action already performed by the current thread is
+    //  *     taken to happen before the volatile write to {@code V}.
+    //  *     (In some implementations, this means that the current thread
+    //  *     performs a global release operation.)
+    //  * <li>Specifically, the write to the current target of {@code S} is
+    //  *     taken to happen before the volatile write to {@code V}.
+    //  * <li>The volatile write to {@code V} is placed
+    //  *     (in an implementation specific manner)
+    //  *     in the global synchronization order.
+    //  * <li>Consider an arbitrary thread {@code T} (other than the current thread).
+    //  *     If {@code T} executes a synchronization action {@code A}
+    //  *     after the volatile write to {@code V} (in the global synchronization order),
+    //  *     it is therefore required to see either the current target
+    //  *     of {@code S}, or a later write to that target,
+    //  *     if it executes a read on the target of {@code S}.
+    //  *     (This constraint is called "synchronization-order consistency".)
+    //  * <li>The JMM specifically allows optimizing compilers to elide
+    //  *     reads or writes of variables that are known to be useless.
+    //  *     Such elided reads and writes have no effect on the happens-before
+    //  *     relation.  Regardless of this fact, the volatile {@code V}
+    //  *     will not be elided, even though its written value is
+    //  *     indeterminate and its read value is not used.
+    //  * </ul>
+    //  * Because of the last point, the implementation behaves as if a
+    //  * volatile read of {@code V} were performed by {@code T}
+    //  * immediately after its action {@code A}.  In the local ordering
+    //  * of actions in {@code T}, this read happens before any future
+    //  * read of the target of {@code S}.  It is as if the
+    //  * implementation arbitrarily picked a read of {@code S}'s target
+    //  * by {@code T}, and forced a read of {@code V} to precede it,
+    //  * thereby ensuring communication of the new target value.
+    //  * <p>
+    //  * As long as the constraints of the Java Memory Model are obeyed,
+    //  * implementations may delay the completion of a {@code syncAll}
+    //  * operation while other threads ({@code T} above) continue to
+    //  * use previous values of {@code S}'s target.
+    //  * However, implementations are (as always) encouraged to avoid
+    //  * livelock, and to eventually require all threads to take account
+    //  * of the updated target.
+    //  *
+    //  * <p style="font-size:smaller;">
+    //  * <em>Discussion:</em>
+    //  * For performance reasons, {@code syncAll} is not a virtual method
+    //  * on a single call site, but rather applies to a set of call sites.
+    //  * Some implementations may incur a large fixed overhead cost
+    //  * for processing one or more synchronization operations,
+    //  * but a small incremental cost for each additional call site.
+    //  * In any case, this operation is likely to be costly, since
+    //  * other threads may have to be somehow interrupted
+    //  * in order to make them notice the updated target value.
+    //  * However, it may be observed that a single call to synchronize
+    //  * several sites has the same formal effect as many calls,
+    //  * each on just one of the sites.
+    //  *
+    //  * <p style="font-size:smaller;">
+    //  * <em>Implementation Note:</em>
+    //  * Simple implementations of {@code MutableCallSite} may use
+    //  * a volatile variable for the target of a mutable call site.
+    //  * In such an implementation, the {@code syncAll} method can be a no-op,
+    //  * and yet it will conform to the JMM behavior documented above.
+    //  *
+    //  * @param sites an array of call sites to be synchronized
+    //  * @throws NullPointerException if the {@code sites} array reference is null
+    //  *                              or the array contains a null
+    //  */
+    // public static void syncAll(MutableCallSite[] sites) {
+    //     if (sites.length == 0)  return;
+    //     STORE_BARRIER.lazySet(0);
+    //     for (int i = 0; i < sites.length; i++) {
+    //         sites[i].getClass();  // trigger NPE on first null
+    //     }
+    //     // FIXME: NYI
+    // }
+    // private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 10ce855..82cc797 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -237,27 +237,25 @@
      */
     public static class ReferenceArrayElementGetter extends Transformer {
         private final Class<?> arrayClass;
-        private final StackFrameReader reader;
-        private final StackFrameWriter writer;
 
         public ReferenceArrayElementGetter(Class<?> arrayClass) {
             super(MethodType.methodType(arrayClass.getComponentType(),
                     new Class<?>[]{arrayClass, int.class}));
             this.arrayClass = arrayClass;
-            reader = new StackFrameReader();
-            writer = new StackFrameWriter();
         }
 
         @Override
         public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+            final StackFrameReader reader = new StackFrameReader();
             reader.attach(emulatedStackFrame);
-            writer.attach(emulatedStackFrame);
 
             // Read the array object and the index from the stack frame.
             final Object[] array = (Object[]) reader.nextReference(arrayClass);
             final int index = reader.nextInt();
 
             // Write the array element back to the stack frame.
+            final StackFrameWriter writer = new StackFrameWriter();
+            writer.attach(emulatedStackFrame);
             writer.makeReturnValueAccessor();
             writer.putNextReference(array[index], arrayClass.getComponentType());
         }
@@ -268,17 +266,16 @@
      */
     public static class ReferenceArrayElementSetter extends Transformer {
         private final Class<?> arrayClass;
-        private final StackFrameReader reader;
 
         public ReferenceArrayElementSetter(Class<?> arrayClass) {
             super(MethodType.methodType(void.class,
                     new Class<?>[] { arrayClass, int.class, arrayClass.getComponentType() }));
             this.arrayClass = arrayClass;
-            reader = new StackFrameReader();
         }
 
         @Override
         public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+            final StackFrameReader reader = new StackFrameReader();
             reader.attach(emulatedStackFrame);
 
             // Read the array object, index and the value to write from the stack frame.
@@ -295,23 +292,20 @@
      */
     public static class ReferenceIdentity extends Transformer {
         private final Class<?> type;
-        private final StackFrameReader reader;
-        private final StackFrameWriter writer;
 
         public ReferenceIdentity(Class<?> type) {
             super(MethodType.methodType(type, type));
             this.type = type;
-
-            reader = new StackFrameReader();
-            writer = new StackFrameWriter();
         }
 
         @Override
         public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+            final StackFrameReader reader = new StackFrameReader();
             reader.attach(emulatedStackFrame);
+
+            final StackFrameWriter writer = new StackFrameWriter();
             writer.attach(emulatedStackFrame);
             writer.makeReturnValueAccessor();
-
             writer.putNextReference(reader.nextReference(type), type);
         }
     }
@@ -336,8 +330,6 @@
 
         private char typeChar;
 
-        private final EmulatedStackFrame.StackFrameWriter writer;
-
         public Constant(Class<?> type, Object value) {
             super(MethodType.methodType(type));
             this.type = type;
@@ -372,12 +364,11 @@
             } else {
                 throw new AssertionError("unknown type: " + typeChar);
             }
-
-            writer = new EmulatedStackFrame.StackFrameWriter();
         }
 
         @Override
         public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+            final StackFrameWriter writer = new StackFrameWriter();
             writer.attach(emulatedStackFrame);
             writer.makeReturnValueAccessor();
 
@@ -491,9 +482,6 @@
         private final MethodHandle target;
         private final MethodHandle filter;
 
-        private final EmulatedStackFrame.StackFrameReader returnValueReader;
-        private final EmulatedStackFrame.StackFrameWriter filterWriter;
-
         private final EmulatedStackFrame.Range allArgs;
 
         public FilterReturnValue(MethodHandle target, MethodHandle filter) {
@@ -502,9 +490,6 @@
             this.target = target;
             this.filter = filter;
 
-            returnValueReader = new EmulatedStackFrame.StackFrameReader();
-            filterWriter = new EmulatedStackFrame.StackFrameWriter();
-
             allArgs = EmulatedStackFrame.Range.all(type());
         }
 
@@ -515,15 +500,16 @@
             // the same parameter shapes.
             EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
             emulatedStackFrame.copyRangeTo(targetFrame, allArgs, 0, 0);
-
             target.invoke(targetFrame);
 
             // Perform the invoke.
+            final StackFrameReader returnValueReader = new StackFrameReader();
             returnValueReader.attach(targetFrame);
             returnValueReader.makeReturnValueAccessor();
 
             // Create an emulated frame for the filter and copy all its arguments across.
             EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
+            final StackFrameWriter filterWriter = new StackFrameWriter();
             filterWriter.attach(filterFrame);
 
             final Class<?> returnType = target.type().rtype();
@@ -563,30 +549,24 @@
         private final MethodHandle target;
         private final int[] reorder;
 
-        private final EmulatedStackFrame.StackFrameWriter writer;
-        private final EmulatedStackFrame.StackFrameReader reader;
-
         public PermuteArguments(MethodType type, MethodHandle target, int[] reorder) {
             super(type);
 
             this.target = target;
             this.reorder = reorder;
-
-            writer = new EmulatedStackFrame.StackFrameWriter();
-            reader = new EmulatedStackFrame.StackFrameReader();
         }
 
         @Override
         public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
+            final StackFrameReader reader = new StackFrameReader();
             reader.attach(emulatedStackFrame);
-            final Class<?>[] ptypes = type().ptypes();
 
             // In the interests of simplicity, we box / unbox arguments while performing
             // the permutation. We first iterate through the incoming stack frame and box
             // each argument. We then unbox and write out the argument to the target frame
             // according to the specified reordering.
             Object[] arguments = new Object[reorder.length];
-
+            final Class<?>[] ptypes = type().ptypes();
             for (int i = 0; i < ptypes.length; ++i) {
                 final Class<?> ptype = ptypes[i];
                 if (!ptype.isPrimitive()) {
@@ -613,6 +593,7 @@
             }
 
             EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(target.type());
+            final StackFrameWriter writer = new StackFrameWriter();
             writer.attach(calleeFrame);
 
             for (int i = 0; i < ptypes.length; ++i) {
@@ -1083,7 +1064,6 @@
          * arguments that aren't a part of the trailing array.
          */
         private final Range copyRange;
-        private final StackFrameWriter writer;
 
         Spreader(MethodHandle target, MethodType spreaderType, int numArrayArgs) {
             super(spreaderType);
@@ -1102,7 +1082,6 @@
             this.numArrayArgs = numArrayArgs;
             // Copy all args except for the last argument.
             this.copyRange = EmulatedStackFrame.Range.of(spreaderType, 0, arrayOffset);
-            this.writer = new StackFrameWriter();
         }
 
         @Override
@@ -1115,6 +1094,7 @@
 
             // Attach the writer, prepare to spread the trailing array arguments into
             // the callee frame.
+            StackFrameWriter writer = new StackFrameWriter();
             writer.attach(targetFrame,
                     arrayOffset,
                     copyRange.numReferences,
@@ -1350,9 +1330,6 @@
          */
         private final Range copyRange;
 
-        private final StackFrameWriter writer;
-        private final StackFrameReader reader;
-
         Collector(MethodHandle delegate, Class<?> arrayType, int length) {
             super(delegate.type().asCollectorType(arrayType, length));
 
@@ -1365,8 +1342,6 @@
 
             // Copy all args except for the last argument.
             copyRange = EmulatedStackFrame.Range.of(delegate.type(), 0, arrayOffset);
-            writer = new StackFrameWriter();
-            reader = new StackFrameReader();
         }
 
         @Override
@@ -1379,7 +1354,9 @@
 
             // Attach the writer, prepare to spread the trailing array arguments into
             // the callee frame.
+            final StackFrameWriter writer = new StackFrameWriter();
             writer.attach(targetFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
+            final StackFrameReader reader = new StackFrameReader();
             reader.attach(callerFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
 
             switch (arrayTypeChar) {
@@ -1481,12 +1458,6 @@
         /** The list of filters to apply */
         private final MethodHandle[] filters;
 
-        private final StackFrameWriter writer;
-        private final StackFrameReader reader;
-
-        private final StackFrameWriter filterWriter;
-        private final StackFrameReader filterReader;
-
         FilterArguments(MethodHandle target, int pos, MethodHandle[] filters) {
             super(deriveType(target, pos, filters));
 
@@ -1494,11 +1465,6 @@
             this.pos = pos;
             this.filters = filters;
 
-            this.writer = new StackFrameWriter();
-            this.reader = new StackFrameReader();
-
-            this.filterReader = new StackFrameReader();
-            this.filterWriter = new StackFrameWriter();
         }
 
         private static MethodType deriveType(MethodHandle target, int pos, MethodHandle[] filters) {
@@ -1512,9 +1478,11 @@
 
         @Override
         public void transform(EmulatedStackFrame stackFrame) throws Throwable {
+            final StackFrameReader reader = new StackFrameReader();
             reader.attach(stackFrame);
 
             EmulatedStackFrame transformedFrame = EmulatedStackFrame.create(target.type());
+            final StackFrameWriter writer = new StackFrameWriter();
             writer.attach(transformedFrame);
 
             final Class<?>[] ptypes = target.type().ptypes();
@@ -1537,12 +1505,14 @@
                     EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
 
                     //  Copy the next argument from the stack frame to the filter frame.
+                    final StackFrameWriter filterWriter = new StackFrameWriter();
                     filterWriter.attach(filterFrame);
                     copyNext(reader, filterWriter, filter.type().ptypes()[0]);
 
                     filter.invoke(filterFrame);
 
                     // Copy the argument back from the filter frame to the stack frame.
+                    final StackFrameReader filterReader = new StackFrameReader();
                     filterReader.attach(filterFrame);
                     filterReader.makeReturnValueAccessor();
                     copyNext(filterReader, writer, ptype);
@@ -1581,9 +1551,6 @@
          */
         private final Range range2;
 
-        private final StackFrameReader reader;
-        private final StackFrameWriter writer;
-
         private final int referencesOffset;
         private final int stackFrameOffset;
 
@@ -1606,9 +1573,6 @@
                 this.range2 = null;
             }
 
-            reader = new StackFrameReader();
-            writer = new StackFrameWriter();
-
             // Calculate the number of primitive bytes (or references) we copy to the
             // target frame based on the return value of the combiner.
             final Class<?> collectorRType = collector.type().rtype();
@@ -1637,7 +1601,9 @@
 
             // If one of these offsets is not zero, we have a return value to copy.
             if (referencesOffset != 0 || stackFrameOffset != 0) {
+                final StackFrameReader reader = new StackFrameReader();
                 reader.attach(filterFrame).makeReturnValueAccessor();
+                final StackFrameWriter writer = new StackFrameWriter();
                 writer.attach(targetFrame, pos, range1.numReferences, range1.numBytes);
                 copyNext(reader, writer, target.type().ptypes()[0]);
             }
@@ -1663,9 +1629,6 @@
         private final Range combinerArgs;
         private final Range targetArgs;
 
-        private final StackFrameReader reader;
-        private final StackFrameWriter writer;
-
         private final int referencesOffset;
         private final int stackFrameOffset;
 
@@ -1678,9 +1641,6 @@
             combinerArgs = Range.all(combiner.type());
             targetArgs = Range.all(type());
 
-            reader = new StackFrameReader();
-            writer = new StackFrameWriter();
-
             final Class<?> combinerRType = combiner.type().rtype();
             if (combinerRType == void.class) {
                 stackFrameOffset = 0;
@@ -1706,7 +1666,9 @@
 
             // If one of these offsets is not zero, we have a return value to copy.
             if (referencesOffset != 0 || stackFrameOffset != 0) {
+                final StackFrameReader reader = new StackFrameReader();
                 reader.attach(combinerFrame).makeReturnValueAccessor();
+                final StackFrameWriter writer = new StackFrameWriter();
                 writer.attach(targetFrame);
                 copyNext(reader, writer, target.type().ptypes()[0]);
             }
@@ -1736,7 +1698,6 @@
 
         private final Range range1;
         private final Range range2;
-        private final StackFrameWriter writer;
 
         InsertArguments(MethodHandle target, int pos, Object[] values) {
             super(target.type().dropParameterTypes(pos, pos + values.length));
@@ -1747,8 +1708,6 @@
             final MethodType type = type();
             range1 = EmulatedStackFrame.Range.of(type, 0, pos);
             range2 = Range.of(type, pos, type.parameterCount());
-
-            writer = new StackFrameWriter();
         }
 
         @Override
@@ -1760,6 +1719,7 @@
 
             // Attach a stack frame writer so that we can copy the next |values.length|
             // arguments.
+            final StackFrameWriter writer = new StackFrameWriter();
             writer.attach(calleeFrame, pos, range1.numReferences, range1.numBytes);
 
             // Copy all the arguments supplied in |values|.
diff --git a/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java b/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java
new file mode 100644
index 0000000..6444335
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+// Android-changed: removed references to MutableCallSite.syncAll().
+/**
+ * A {@code VolatileCallSite} is a {@link CallSite} whose target acts like a volatile variable.
+ * An {@code invokedynamic} instruction linked to a {@code VolatileCallSite} sees updates
+ * to its call site target immediately, even if the update occurs in another thread.
+ * There may be a performance penalty for such tight coupling between threads.
+ * <p>
+ * In other respects, a {@code VolatileCallSite} is interchangeable
+ * with {@code MutableCallSite}.
+ * @see MutableCallSite
+ * @author John Rose, JSR 292 EG
+ */
+public class VolatileCallSite extends CallSite {
+    /**
+     * Creates a call site with a volatile binding to its target.
+     * The initial target is set to a method handle
+     * of the given type which will throw an {@code IllegalStateException} if called.
+     * @param type the method type that this call site will have
+     * @throws NullPointerException if the proposed type is null
+     */
+    public VolatileCallSite(MethodType type) {
+        super(type);
+    }
+
+    /**
+     * Creates a call site with a volatile binding to its target.
+     * The target is set to the given value.
+     * @param target the method handle that will be the initial target of the call site
+     * @throws NullPointerException if the proposed target is null
+     */
+    public VolatileCallSite(MethodHandle target) {
+        super(target);
+    }
+
+    /**
+     * Returns the target method of the call site, which behaves
+     * like a {@code volatile} field of the {@code VolatileCallSite}.
+     * <p>
+     * The interactions of {@code getTarget} with memory are the same
+     * as of a read from a {@code volatile} field.
+     * <p>
+     * In particular, the current thread is required to issue a fresh
+     * read of the target from memory, and must not fail to see
+     * a recent update to the target by another thread.
+     *
+     * @return the linkage state of this call site, a method handle which can change over time
+     * @see #setTarget
+     */
+    @Override public final MethodHandle getTarget() {
+        return getTargetVolatile();
+    }
+
+    /**
+     * Updates the target method of this call site, as a volatile variable.
+     * The type of the new target must agree with the type of the old target.
+     * <p>
+     * The interactions with memory are the same as of a write to a volatile field.
+     * In particular, any threads is guaranteed to see the updated target
+     * the next time it calls {@code getTarget}.
+     * @param newTarget the new target
+     * @throws NullPointerException if the proposed new target is null
+     * @throws WrongMethodTypeException if the proposed new target
+     *         has a method type that differs from the previous target
+     * @see #getTarget
+     */
+    @Override public void setTarget(MethodHandle newTarget) {
+        checkTargetChange(getTargetVolatile(), newTarget);
+        setTargetVolatile(newTarget);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final MethodHandle dynamicInvoker() {
+        return makeDynamicInvoker();
+    }
+}
diff --git a/ojluni/src/main/java/java/lang/ref/Reference.java b/ojluni/src/main/java/java/lang/ref/Reference.java
index 31f37e5..99adb36 100644
--- a/ojluni/src/main/java/java/lang/ref/Reference.java
+++ b/ojluni/src/main/java/java/lang/ref/Reference.java
@@ -26,6 +26,8 @@
 
 package java.lang.ref;
 
+import dalvik.annotation.optimization.FastNative;
+
 
 /**
  * Abstract base class for reference objects.  This class defines the
@@ -96,6 +98,7 @@
         return getReferent();
     }
 
+    @FastNative
     private final native T getReferent();
 
     /**
@@ -111,6 +114,7 @@
 
     // Direct access to the referent is prohibited, clearReferent blocks and set
     // the referent to null when it is safe to do so.
+    @FastNative
     native void clearReferent();
 
     /* -- Queue operations -- */
diff --git a/ojluni/src/main/java/java/lang/reflect/Array.java b/ojluni/src/main/java/java/lang/reflect/Array.java
index c266060..48863d9 100644
--- a/ojluni/src/main/java/java/lang/reflect/Array.java
+++ b/ojluni/src/main/java/java/lang/reflect/Array.java
@@ -26,6 +26,8 @@
 
 package java.lang.reflect;
 
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * The {@code Array} class provides static methods to dynamically create and
  * access Java arrays.
@@ -721,6 +723,7 @@
     /*
      * Create a multi-dimensional array of objects with the specified type.
      */
+    @FastNative
     private static native Object createMultiArray(Class<?> componentType, int[] dimensions) throws NegativeArraySizeException;
 
     /**
@@ -760,6 +763,7 @@
     /*
      * Create a one-dimensional array of objects with the specified type.
      */
+    @FastNative
     private static native Object createObjectArray(Class<?> componentType, int length) throws NegativeArraySizeException;
 
     private static IllegalArgumentException notAnArray(Object o) {
diff --git a/ojluni/src/main/java/java/lang/reflect/Constructor.java b/ojluni/src/main/java/java/lang/reflect/Constructor.java
index 7e3a71d..1a99660 100644
--- a/ojluni/src/main/java/java/lang/reflect/Constructor.java
+++ b/ojluni/src/main/java/java/lang/reflect/Constructor.java
@@ -26,6 +26,7 @@
 
 package java.lang.reflect;
 
+import dalvik.annotation.optimization.FastNative;
 import java.lang.annotation.Annotation;
 import java.util.Comparator;
 
@@ -153,6 +154,7 @@
      * {@inheritDoc}
      */
     @Override
+    @FastNative
     public native Class<?>[] getExceptionTypes();
 
     /**
@@ -327,9 +329,11 @@
         }
     }
 
+    @FastNative
     private static native Object newInstanceFromSerialization(Class<?> ctorClass, Class<?> allocClass)
         throws InstantiationException, IllegalArgumentException, InvocationTargetException;
 
+    @FastNative
     private native T newInstance0(Object... args) throws InstantiationException,
             IllegalAccessException, IllegalArgumentException, InvocationTargetException;
 
diff --git a/ojluni/src/main/java/java/lang/reflect/Executable.java b/ojluni/src/main/java/java/lang/reflect/Executable.java
index 539716f..59f50e1 100644
--- a/ojluni/src/main/java/java/lang/reflect/Executable.java
+++ b/ojluni/src/main/java/java/lang/reflect/Executable.java
@@ -25,6 +25,7 @@
 
 package java.lang.reflect;
 
+import dalvik.annotation.optimization.FastNative;
 import com.android.dex.Dex;
 
 import java.lang.annotation.Annotation;
@@ -420,6 +421,7 @@
     private transient volatile boolean hasRealParameterData;
     private transient volatile Parameter[] parameters;
 
+    @FastNative
     private native Parameter[] getParameters0();
 
     /**
@@ -534,6 +536,7 @@
         // Android-changed: Implemented natively.
         return getAnnotationNative(annotationClass);
     }
+    @FastNative
     private native <T extends Annotation> T getAnnotationNative(Class<T> annotationClass);
 
     /**
@@ -554,6 +557,7 @@
         // Android-changed: Implemented natively.
         return getDeclaredAnnotationsNative();
     }
+    @FastNative
     private native Annotation[] getDeclaredAnnotationsNative();
 
     // Android-changed: Additional ART-related fields and logic below that is shared between
@@ -649,6 +653,7 @@
         Objects.requireNonNull(annotationType);
         return isAnnotationPresentNative(annotationType);
     }
+    @FastNative
     private native boolean isAnnotationPresentNative(Class<? extends Annotation> annotationType);
 
     final Annotation[][] getParameterAnnotationsInternal() {
@@ -658,6 +663,7 @@
         }
         return parameterAnnotations;
     }
+    @FastNative
     private native Annotation[][] getParameterAnnotationsNative();
 
     /**
@@ -721,6 +727,7 @@
         }
         return result.toString();
     }
+    @FastNative
     private native String[] getSignatureAnnotation();
 
     final boolean equalNameAndParametersInternal(Method m) {
diff --git a/ojluni/src/main/java/java/lang/reflect/Field.java b/ojluni/src/main/java/java/lang/reflect/Field.java
index 111fa2b..2f1bc31 100644
--- a/ojluni/src/main/java/java/lang/reflect/Field.java
+++ b/ojluni/src/main/java/java/lang/reflect/Field.java
@@ -26,6 +26,7 @@
 
 package java.lang.reflect;
 
+import dalvik.annotation.optimization.FastNative;
 import com.android.dex.Dex;
 
 import java.lang.annotation.Annotation;
@@ -183,6 +184,7 @@
         }
         return result.toString();
     }
+    @FastNative
     private native String[] getSignatureAnnotation();
 
 
@@ -316,6 +318,7 @@
      * @exception ExceptionInInitializerError if the initialization provoked
      *              by this method fails.
      */
+    @FastNative
     public native Object get(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -341,6 +344,7 @@
      *              by this method fails.
      * @see       Field#get
      */
+    @FastNative
     public native boolean getBoolean(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -366,6 +370,7 @@
      *              by this method fails.
      * @see       Field#get
      */
+    @FastNative
     public native byte getByte(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -393,6 +398,7 @@
      *              by this method fails.
      * @see Field#get
      */
+    @FastNative
     public native char getChar(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -420,6 +426,7 @@
      *              by this method fails.
      * @see       Field#get
      */
+    @FastNative
     public native short getShort(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -447,6 +454,7 @@
      *              by this method fails.
      * @see       Field#get
      */
+    @FastNative
     public native int getInt(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -474,6 +482,7 @@
      *              by this method fails.
      * @see       Field#get
      */
+    @FastNative
     public native long getLong(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -501,6 +510,7 @@
      *              by this method fails.
      * @see Field#get
      */
+    @FastNative
     public native float getFloat(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -528,6 +538,7 @@
      *              by this method fails.
      * @see       Field#get
      */
+    @FastNative
     public native double getDouble(Object obj)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -597,6 +608,7 @@
      * @exception ExceptionInInitializerError if the initialization provoked
      *              by this method fails.
      */
+    @FastNative
     public native void set(Object obj, Object value)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -624,6 +636,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setBoolean(Object obj, boolean z)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -651,6 +664,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setByte(Object obj, byte b)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -678,6 +692,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setChar(Object obj, char c)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -705,6 +720,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setShort(Object obj, short s)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -732,6 +748,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setInt(Object obj, int i)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -759,6 +776,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setLong(Object obj, long l)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -786,6 +804,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setFloat(Object obj, float f)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -813,6 +832,7 @@
      *              by this method fails.
      * @see       Field#set
      */
+    @FastNative
     public native void setDouble(Object obj, double d)
         throws IllegalArgumentException, IllegalAccessException;
 
@@ -825,6 +845,7 @@
         Objects.requireNonNull(annotationClass);
         return getAnnotationNative(annotationClass);
     }
+    @FastNative
     private native <A extends Annotation> A getAnnotationNative(Class<A> annotationType);
 
     /**
@@ -845,12 +866,14 @@
         }
         return isAnnotationPresentNative(annotationType);
     }
+    @FastNative
     private native boolean isAnnotationPresentNative(Class<? extends Annotation> annotationType);
 
     /**
      * {@inheritDoc}
      */
     @Override
+    @FastNative
     public native Annotation[] getDeclaredAnnotations();
 
     /**
@@ -874,5 +897,6 @@
     /**
      * @hide - export for use by {@code java.lang.invoke.*}
      */
+    @FastNative
     public native long getArtField();
 }
diff --git a/ojluni/src/main/java/java/lang/reflect/Method.java b/ojluni/src/main/java/java/lang/reflect/Method.java
index e3fa1c9..4d47edf 100644
--- a/ojluni/src/main/java/java/lang/reflect/Method.java
+++ b/ojluni/src/main/java/java/lang/reflect/Method.java
@@ -26,6 +26,7 @@
 
 package java.lang.reflect;
 
+import dalvik.annotation.optimization.FastNative;
 import java.lang.annotation.Annotation;
 import java.util.Comparator;
 import libcore.reflect.Types;
@@ -202,6 +203,7 @@
      * {@inheritDoc}
      */
     @Override
+    @FastNative
     public native Class<?>[] getExceptionTypes();
 
     /**
@@ -395,6 +397,7 @@
      * @exception ExceptionInInitializerError if the initialization
      * provoked by this method fails.
      */
+    @FastNative
     public native Object invoke(Object obj, Object... args)
             throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
 
@@ -461,6 +464,7 @@
      *     default class value.
      * @since  1.5
      */
+    @FastNative
     public native Object getDefaultValue();
 
     /**
diff --git a/ojluni/src/main/java/java/lang/reflect/Parameter.java b/ojluni/src/main/java/java/lang/reflect/Parameter.java
index 2d96307..77c26d7 100644
--- a/ojluni/src/main/java/java/lang/reflect/Parameter.java
+++ b/ojluni/src/main/java/java/lang/reflect/Parameter.java
@@ -24,6 +24,7 @@
  */
 package java.lang.reflect;
 
+import dalvik.annotation.optimization.FastNative;
 import java.lang.annotation.*;
 import java.util.HashMap;
 import java.util.Map;
@@ -273,6 +274,7 @@
         // Android-changed: Uses native code to obtain annotation information.
         return getAnnotationNative(executable, index, annotationClass);
     }
+    @FastNative
     private static native <A extends Annotation> A getAnnotationNative(
             Executable executable, int parameterIndex, Class<A> annotationType);
 
diff --git a/ojluni/src/main/java/java/lang/reflect/Proxy.java b/ojluni/src/main/java/java/lang/reflect/Proxy.java
index c9e56cf..90a6651 100644
--- a/ojluni/src/main/java/java/lang/reflect/Proxy.java
+++ b/ojluni/src/main/java/java/lang/reflect/Proxy.java
@@ -27,6 +27,7 @@
 package java.lang.reflect;
 
 
+import dalvik.annotation.optimization.FastNative;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 import java.security.Permission;
@@ -912,6 +913,7 @@
         return h.invoke(proxy, method, args);
     }
 
+    @FastNative
     private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                  ClassLoader loader, Method[] methods,
                                                  Class<?>[][] exceptions);
diff --git a/ojluni/src/main/java/java/net/URLConnection.java b/ojluni/src/main/java/java/net/URLConnection.java
index 3215f6b..127c289 100644
--- a/ojluni/src/main/java/java/net/URLConnection.java
+++ b/ojluni/src/main/java/java/net/URLConnection.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1511,7 +1511,7 @@
         }
 
         if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) {
-            if (c4 == 0xE0) {
+            if (c4 == 0xE0 || c4 == 0xEE) {
                 return "image/jpeg";
             }
 
@@ -1526,10 +1526,6 @@
                  c11 == 0)) {
                 return "image/jpeg";
             }
-
-            if (c4 == 0xEE) {
-                return "image/jpg";
-            }
         }
 
         if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0 &&
diff --git a/ojluni/src/main/java/java/nio/DirectByteBuffer.java b/ojluni/src/main/java/java/nio/DirectByteBuffer.java
index 7007e15..fc40f26 100644
--- a/ojluni/src/main/java/java/nio/DirectByteBuffer.java
+++ b/ojluni/src/main/java/java/nio/DirectByteBuffer.java
@@ -53,6 +53,7 @@
         long allocatedAddress;
         final int offset;
         boolean isAccessible;
+        boolean isFreed;
 
         MemoryRef(int capacity) {
             VMRuntime runtime = VMRuntime.getRuntime();
@@ -61,6 +62,7 @@
             // Offset is set to handle the alignment: http://b/16449607
             offset = (int) (((allocatedAddress + 7) & ~(long) 7) - allocatedAddress);
             isAccessible = true;
+            isFreed = false;
         }
 
         MemoryRef(long allocatedAddress) {
@@ -74,6 +76,7 @@
             buffer = null;
             allocatedAddress = 0;
             isAccessible = false;
+            isFreed = true;
         }
     }
 
@@ -154,8 +157,8 @@
 
     @Override
     public final ByteBuffer duplicate() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         return new DirectByteBuffer(memoryRef,
                 this.markValue(),
@@ -168,8 +171,8 @@
 
     @Override
     public final ByteBuffer asReadOnlyBuffer() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         return new DirectByteBuffer(memoryRef,
                 this.markValue(),
@@ -235,24 +238,24 @@
 
     @Override
     public final ByteBuffer put(byte x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         put(ix(nextPutIndex()), x);
         return this;
     }
 
     @Override
     public final ByteBuffer put(int i, byte x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         put(ix(checkIndex(i)), x);
         return this;
     }
@@ -260,12 +263,12 @@
     // This method is not declared final because it is overridden in tests.
     @Override
     public ByteBuffer put(byte[] src, int srcOffset, int length) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         checkBounds(srcOffset, length, src.length);
         int pos = position();
         int lim = limit();
@@ -281,12 +284,12 @@
 
     @Override
     public final ByteBuffer compact() {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         int pos = position();
         int lim = limit();
         assert (pos <= lim);
@@ -345,11 +348,17 @@
 
     @Override
     char getCharUnchecked(int i) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         return (char) Memory.peekShort(ix(i), !nativeByteOrder);
     }
 
     @Override
     void getUnchecked(int pos, char[] dst, int dstOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.peekCharArray(ix(pos),
                 dst, dstOffset, length, !nativeByteOrder);
     }
@@ -361,43 +370,49 @@
 
     @Override
     public final ByteBuffer putChar(char x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putChar(ix(nextPutIndex(SizeOf.CHAR)), x);
         return this;
     }
 
     @Override
     public final ByteBuffer putChar(int i, char x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putChar(ix(checkIndex(i, SizeOf.CHAR)), x);
         return this;
     }
 
     @Override
     void putCharUnchecked(int i, char x) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         putChar(ix(i), x);
     }
 
     @Override
     void putUnchecked(int pos, char[] src, int srcOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.pokeCharArray(ix(pos),
                 src, srcOffset, length, !nativeByteOrder);
     }
 
     @Override
     public final CharBuffer asCharBuffer() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         int off = this.position();
         int lim = this.limit();
@@ -435,11 +450,17 @@
 
     @Override
     short getShortUnchecked(int i) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         return getShort(ix(i));
     }
 
     @Override
     void getUnchecked(int pos, short[] dst, int dstOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.peekShortArray(ix(pos),
                 dst, dstOffset, length, !nativeByteOrder);
     }
@@ -451,43 +472,49 @@
 
     @Override
     public final ByteBuffer putShort(short x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putShort(ix(nextPutIndex(SizeOf.SHORT)), x);
         return this;
     }
 
     @Override
     public final ByteBuffer putShort(int i, short x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putShort(ix(checkIndex(i, SizeOf.SHORT)), x);
         return this;
     }
 
     @Override
     void putShortUnchecked(int i, short x) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         putShort(ix(i), x);
     }
 
     @Override
     void putUnchecked(int pos, short[] src, int srcOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.pokeShortArray(ix(pos),
                 src, srcOffset, length, !nativeByteOrder);
     }
 
     @Override
     public final ShortBuffer asShortBuffer() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         int off = this.position();
         int lim = this.limit();
@@ -525,11 +552,17 @@
 
     @Override
     final int getIntUnchecked(int i) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         return getInt(ix(i));
     }
 
     @Override
     final void getUnchecked(int pos, int[] dst, int dstOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.peekIntArray(ix(pos),
                 dst, dstOffset, length, !nativeByteOrder);
     }
@@ -541,43 +574,49 @@
 
     @Override
     public final ByteBuffer putInt(int x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putInt(ix(nextPutIndex(SizeOf.INT)), x);
         return this;
     }
 
     @Override
     public final ByteBuffer putInt(int i, int x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putInt(ix(checkIndex(i, SizeOf.INT)), x);
         return this;
     }
 
     @Override
     final void putIntUnchecked(int i, int x) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         putInt(ix(i), x);
     }
 
     @Override
     final void putUnchecked(int pos, int[] src, int srcOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.pokeIntArray(ix(pos),
                 src, srcOffset, length, !nativeByteOrder);
     }
 
     @Override
     public final IntBuffer asIntBuffer() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         int off = this.position();
         int lim = this.limit();
@@ -615,11 +654,17 @@
 
     @Override
     final long getLongUnchecked(int i) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         return getLong(ix(i));
     }
 
     @Override
     final void getUnchecked(int pos, long[] dst, int dstOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.peekLongArray(ix(pos),
                 dst, dstOffset, length, !nativeByteOrder);
     }
@@ -631,43 +676,49 @@
 
     @Override
     public final ByteBuffer putLong(long x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putLong(ix(nextPutIndex(SizeOf.LONG)), x);
         return this;
     }
 
     @Override
     public final ByteBuffer putLong(int i, long x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putLong(ix(checkIndex(i, SizeOf.LONG)), x);
         return this;
     }
 
     @Override
     final void putLongUnchecked(int i, long x) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         putLong(ix(i), x);
     }
 
     @Override
     final void putUnchecked(int pos, long[] src, int srcOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.pokeLongArray(ix(pos),
                 src, srcOffset, length, !nativeByteOrder);
     }
 
     @Override
     public final LongBuffer asLongBuffer() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         int off = this.position();
         int lim = this.limit();
@@ -706,11 +757,17 @@
 
     @Override
     final float getFloatUnchecked(int i) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         return getFloat(ix(i));
     }
 
     @Override
     final void getUnchecked(int pos, float[] dst, int dstOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.peekFloatArray(ix(pos),
                 dst, dstOffset, length, !nativeByteOrder);
     }
@@ -723,43 +780,49 @@
 
     @Override
     public final ByteBuffer putFloat(float x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putFloat(ix(nextPutIndex(SizeOf.FLOAT)), x);
         return this;
     }
 
     @Override
     public final ByteBuffer putFloat(int i, float x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putFloat(ix(checkIndex(i, SizeOf.FLOAT)), x);
         return this;
     }
 
     @Override
     final void putFloatUnchecked(int i, float x) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         putFloat(ix(i), x);
     }
 
     @Override
     final void putUnchecked(int pos, float[] src, int srcOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.pokeFloatArray(ix(pos),
                 src, srcOffset, length, !nativeByteOrder);
     }
 
     @Override
     public final FloatBuffer asFloatBuffer() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         int off = this.position();
         int lim = this.limit();
@@ -798,11 +861,17 @@
 
     @Override
     final double getDoubleUnchecked(int i) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         return getDouble(ix(i));
     }
 
     @Override
     final void getUnchecked(int pos, double[] dst, int dstOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.peekDoubleArray(ix(pos),
                 dst, dstOffset, length, !nativeByteOrder);
     }
@@ -815,43 +884,49 @@
 
     @Override
     public final ByteBuffer putDouble(double x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putDouble(ix(nextPutIndex(SizeOf.DOUBLE)), x);
         return this;
     }
 
     @Override
     public final ByteBuffer putDouble(int i, double x) {
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (!memoryRef.isAccessible) {
             throw new IllegalStateException("buffer is inaccessible");
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         putDouble(ix(checkIndex(i, SizeOf.DOUBLE)), x);
         return this;
     }
 
     @Override
     final void putDoubleUnchecked(int i, double x) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         putDouble(ix(i), x);
     }
 
     @Override
     final void putUnchecked(int pos, double[] src, int srcOffset, int length) {
+        if (!memoryRef.isAccessible) {
+            throw new IllegalStateException("buffer is inaccessible");
+        }
         Memory.pokeDoubleArray(ix(pos),
                 src, srcOffset, length, !nativeByteOrder);
     }
 
     @Override
     public final DoubleBuffer asDoubleBuffer() {
-        if (!memoryRef.isAccessible) {
-            throw new IllegalStateException("buffer is inaccessible");
+        if (memoryRef.isFreed) {
+            throw new IllegalStateException("buffer has been freed");
         }
         int off = this.position();
         int lim = this.limit();
diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java
index deb186c..a932893 100644
--- a/ojluni/src/main/java/java/security/Provider.java
+++ b/ojluni/src/main/java/java/security/Provider.java
@@ -895,15 +895,6 @@
             algorithm = algorithm.toUpperCase(ENGLISH);
             this.algorithm = intern ? algorithm.intern() : algorithm;
         }
-        // BEGIN ANDROID-CHANGED
-        // The above constructor is not amenable to compile-time execution as Locale.ENGLISH
-        // cannot be retrieved. Special-case a constructor for the static field "previousKey".
-        private ServiceKey() {
-            this.type = "";
-            this.originalAlgorithm = "";
-            this.algorithm = "";
-        }
-        // END ANDROID-CHANGED
         public int hashCode() {
             return type.hashCode() + algorithm.hashCode();
         }
@@ -1088,10 +1079,7 @@
     // list and queries each provider with the same values until it finds
     // a matching service
     private static volatile ServiceKey previousKey =
-                                            // BEGIN ANDROID-CHANGED
-                                            //  was: new ServiceKey("", "", false);
-                                            new ServiceKey();
-                                            // END ANDROID-CHANGED
+                                            new ServiceKey("", "", false);
 
     /**
      * Get an unmodifiable Set of all services supported by
diff --git a/ojluni/src/main/java/java/text/SimpleDateFormat.java b/ojluni/src/main/java/java/text/SimpleDateFormat.java
index de2a93e..700d5ca 100644
--- a/ojluni/src/main/java/java/text/SimpleDateFormat.java
+++ b/ojluni/src/main/java/java/text/SimpleDateFormat.java
@@ -1821,7 +1821,7 @@
             }
             if (bestMatch == null) {
                 // No match found, return error.
-                return 0;
+                return -start;
             }
         }
 
@@ -1926,7 +1926,7 @@
             }
             return (start + zoneNames[nameIndex].length());
         }
-        return 0;
+        return -start;
     }
 
     /**
diff --git a/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java b/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
index 676421a..5848e94 100644
--- a/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
+++ b/ojluni/src/main/java/java/time/chrono/ChronoLocalDate.java
@@ -715,7 +715,7 @@
      * only compares the underlying date and not the chronology.
      * This allows dates in different calendar systems to be compared based
      * on the time-line position.
-     * This is equivalent to using {@code date1.toEpochDay() &gt; date2.toEpochDay()}.
+     * This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}.
      * <p>
      * This default implementation performs the comparison based on the epoch-day.
      *
@@ -733,7 +733,7 @@
      * only compares the underlying date and not the chronology.
      * This allows dates in different calendar systems to be compared based
      * on the time-line position.
-     * This is equivalent to using {@code date1.toEpochDay() &lt; date2.toEpochDay()}.
+     * This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}.
      * <p>
      * This default implementation performs the comparison based on the epoch-day.
      *
diff --git a/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java b/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
index 15f7d84..29e503e 100644
--- a/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
+++ b/ojluni/src/main/java/java/time/chrono/JapaneseChronology.java
@@ -132,7 +132,7 @@
     private static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
 
     static Calendar createCalendar() {
-        return Calendar.getJapanesImperialInstance(TimeZone.getDefault(), LOCALE);
+        return Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), LOCALE);
     }
 
     /**
diff --git a/ojluni/src/main/java/java/time/format/ZoneName.java b/ojluni/src/main/java/java/time/format/ZoneName.java
index 1d58109..c3eb20a 100644
--- a/ojluni/src/main/java/java/time/format/ZoneName.java
+++ b/ojluni/src/main/java/java/time/format/ZoneName.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,12 +28,8 @@
 import android.icu.text.TimeZoneNames;
 import android.icu.util.TimeZone;
 import android.icu.util.ULocale;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
 
 /**
  * A helper class to map a zone name to metazone and back to the
diff --git a/ojluni/src/main/java/java/util/Calendar.java b/ojluni/src/main/java/java/util/Calendar.java
index 60c1c97..8593403 100644
--- a/ojluni/src/main/java/java/util/Calendar.java
+++ b/ojluni/src/main/java/java/util/Calendar.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1161,12 +1161,13 @@
             return this;
         }
 
+        // Android-changed: fix typo in example code.
         /**
          * Sets field parameters to their values given by
          * {@code fieldValuePairs} that are pairs of a field and its value.
          * For example,
          * <pre>
-         *   setFeilds(Calendar.YEAR, 2013,
+         *   setFields(Calendar.YEAR, 2013,
          *             Calendar.MONTH, Calendar.DECEMBER,
          *             Calendar.DAY_OF_MONTH, 23);</pre>
          * is equivalent to the sequence of the following
@@ -1464,12 +1465,17 @@
                 type = locale.getUnicodeLocaleType("ca");
             }
             if (type == null) {
+                // Android-changed: don't switch to buddhist calendar based on locale.
+                // See http://b/35138741
+                /*
                 if (locale.getCountry() == "TH"
-                    && locale.getLanguage() == "th") {
+                        && locale.getLanguage() == "th") {
                     type = "buddhist";
                 } else {
                     type = "gregory";
                 }
+                */
+                type = "gregory";
             }
             switch (type) {
             case "gregory":
@@ -1483,6 +1489,15 @@
                 setWeekDefinition(MONDAY, 4);
                 cal = gcal;
                 break;
+// Android-changed BEGIN: removed support for "buddhist" and "japanese".
+//            case "buddhist":
+//                cal = new BuddhistCalendar(zone, locale);
+//                cal.clear();
+//                break;
+//            case "japanese":
+//                cal = new JapaneseImperialCalendar(zone, locale, true);
+//                break;
+// Android-changed END: removed support for "buddhist" and "japanese".
             default:
                 throw new IllegalArgumentException("unknown calendar type: " + type);
             }
@@ -1579,6 +1594,7 @@
      */
     protected Calendar(TimeZone zone, Locale aLocale)
     {
+        // Android-added BEGIN: Allow aLocale == null
         // http://b/16938922.
         //
         // TODO: This is for backwards compatibility only. Seems like a better idea to throw
@@ -1586,6 +1602,7 @@
         if (aLocale == null) {
             aLocale = Locale.getDefault();
         }
+        // Android-added END: Allow aLocale == null
         fields = new int[FIELD_COUNT];
         isSet = new boolean[FIELD_COUNT];
         stamp = new int[FIELD_COUNT];
@@ -1649,18 +1666,22 @@
         return createCalendar(zone, aLocale);
     }
 
+    // Android-added BEGIN: add getJapaneseImperialInstance()
     /**
      * Create a Japanese Imperial Calendar.
      * @hide
      */
-    public static Calendar getJapanesImperialInstance(TimeZone zone, Locale aLocale) {
+    public static Calendar getJapaneseImperialInstance(TimeZone zone, Locale aLocale) {
         return new JapaneseImperialCalendar(zone, aLocale);
     }
+    // Android-added END: add getJapaneseImperialInstance()
 
     private static Calendar createCalendar(TimeZone zone,
                                            Locale aLocale)
     {
+        // Android-changed BEGIN: only support GregorianCalendar here
         return new GregorianCalendar(zone, aLocale);
+        // Android-changed END: only support GregorianCalendar here
     }
 
     /**
@@ -1751,7 +1772,12 @@
     public void setTimeInMillis(long millis) {
         // If we don't need to recalculate the calendar field values,
         // do nothing.
+// Android-changed BEGIN: Removed ZoneInfo support
         if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet) {
+//        if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
+//            && (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
+// Android-changed END: Removed ZoneInfo support
+
             return;
         }
         time = millis;
@@ -2034,11 +2060,13 @@
      * @since 1.6
      */
     public String getDisplayName(int field, int style, Locale locale) {
-        // Android-changed: Android has traditionally treated ALL_STYLES as SHORT, even though
+        // Android-changed BEGIN: Treat ALL_STYLES as SHORT
+        // Android has traditionally treated ALL_STYLES as SHORT, even though
         // it's not documented to be a valid value for style.
         if (style == ALL_STYLES) {
             style = SHORT;
         }
+        // Android-changed END: Treat ALL_STYLES as SHORT
         if (!checkDisplayNameParams(field, style, SHORT, NARROW_FORMAT, locale,
                             ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
             return null;
@@ -2132,6 +2160,7 @@
                                     ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
             return null;
         }
+        // Android-added: Add complete() here to fix leniency, see http://b/35382060
         complete();
 
         String calendarType = getCalendarType();
@@ -2179,10 +2208,12 @@
             baseStyle < minStyle || baseStyle > maxStyle) {
             throw new IllegalArgumentException();
         }
-        // Android-changed: 3 is not a valid base style (1, 2 and 4 are, though), throw if used.
+        // Android-added BEGIN: Check for invalid baseStyle == 3
+        // 3 is not a valid base style (unlike 1, 2 and 4). Throw if used.
         if (baseStyle == 3) {
             throw new IllegalArgumentException();
         }
+        // Android-added END: Check for invalid baseStyle == 3
         if (locale == null) {
             throw new NullPointerException();
         }
@@ -2595,6 +2626,7 @@
         static {
             Set<String> set = new HashSet<>(3);
             set.add("gregory");
+            // Android-changed: removed "buddhist" and "japanese".
             SET = Collections.unmodifiableSet(set);
         }
         private AvailableCalendarTypes() {
diff --git a/ojluni/src/main/java/java/util/Formatter.java b/ojluni/src/main/java/java/util/Formatter.java
index ca1b990..43d5043 100644
--- a/ojluni/src/main/java/java/util/Formatter.java
+++ b/ojluni/src/main/java/java/util/Formatter.java
@@ -2710,6 +2710,7 @@
             if (s != null) {
                 try {
                     // Android-changed: FormatSpecifierParser passes in correct String.
+                    // index = Integer.parseInt(s.substring(0, s.length() - 1));
                     index = Integer.parseInt(s);
                 } catch (NumberFormatException x) {
                     assert(false);
@@ -2757,6 +2758,8 @@
             precision = -1;
             if (s != null) {
                 try {
+                    // Android-changed: FormatSpecifierParser passes in correct String.
+                    // precision = Integer.parseInt(s.substring(1));
                     precision = Integer.parseInt(s);
                     if (precision < 0)
                         throw new IllegalFormatPrecisionException(precision);
@@ -2789,7 +2792,7 @@
             return c;
         }
 
-        // Android-changed: FormatSpecifierParser passes in the values instead of a Matcher.
+        // Android-changed BEGIN: FormatSpecifierParser passes in the values instead of a Matcher.
         FormatSpecifier(String indexStr, String flagsStr, String widthStr,
                         String precisionStr, String tTStr, String convStr) {
             int idx = 1;
@@ -2806,7 +2809,7 @@
             }
 
             conversion(convStr);
-
+        // Android-changed END: FormatSpecifierParser passes in the values instead of a Matcher.
             if (dt)
                 checkDateTime();
             else if (Conversion.isGeneral(c))
@@ -2998,6 +3001,7 @@
                 s = s.substring(0, precision);
             if (f.contains(Flags.UPPERCASE)) {
                 // Android-changed: Use provided locale instead of default, if it is non-null.
+                // s = s.toUpperCase();
                 s = s.toUpperCase(l != null ? l : Locale.getDefault());
             }
             a.append(justify(s));
@@ -3369,12 +3373,13 @@
                     newW = adjustWidth(width - exp.length - 1, f, neg);
                 localizedMagnitude(sb, mant, f, newW, l);
 
-                // Android-changed: Use localized exponent separator for %e.
+                // Android-changed BEGIN: Use localized exponent separator for %e.
                 Locale separatorLocale = (l != null) ? l : Locale.getDefault();
                 LocaleData localeData = LocaleData.get(separatorLocale);
                 sb.append(f.contains(Flags.UPPERCASE) ?
                         localeData.exponentSeparator.toUpperCase(separatorLocale) :
                         localeData.exponentSeparator.toLowerCase(separatorLocale));
+                // Android-changed END: Use localized exponent separator for %e.
 
                 Flags flags = f.dup().remove(Flags.GROUP);
                 char sign = exp[0];
@@ -4455,14 +4460,14 @@
                     grpSep = dfs.getGroupingSeparator();
                     DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
                     grpSize = df.getGroupingSize();
-                    // Android-changed: http://b/33245708 : Some locales have a group separator but
-                    // also patterns without groups. If we do not clear the group separator in these
-                    // cases a divide by zero is thrown when determining where to place the
-                    // separators.
+                    // Android-changed BEGIN: http://b/33245708
+                    // Some locales have a group separator but also patterns without groups.
+                    // If we do not clear the group separator in these cases a divide by zero
+                    // is thrown when determining where to place the separators.
                     if (!df.isGroupingUsed() || df.getGroupingSize() == 0) {
                         grpSep = '\0';
                     }
-                    // Android-changed: end http://b/33245708.
+                    // Android-changed END: http://b/33245708.
                 }
             }
 
diff --git a/ojluni/src/main/java/java/util/GregorianCalendar.java b/ojluni/src/main/java/java/util/GregorianCalendar.java
index 3d8a7c79..15c7652 100644
--- a/ojluni/src/main/java/java/util/GregorianCalendar.java
+++ b/ojluni/src/main/java/java/util/GregorianCalendar.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -739,10 +739,12 @@
         gdate = (BaseCalendar.Date) gcal.newCalendarDate(getZone());
     }
 
+    // Android-added BEGIN
     GregorianCalendar(long milliseconds) {
         this();
         setTimeInMillis(milliseconds);
     }
+    // Android-added END
 
 /////////////////
 // Public methods
@@ -1078,8 +1080,7 @@
             }
 
             fd += delta; // fd is the expected fixed date after the calculation
-
-            // Android changed: move time zone related calculation to separate method.
+            // Android-changed BEGIN: time zone related calculation via helper methods
             // Calculate the time in the UTC time zone.
             long utcTime = (fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
 
@@ -1092,6 +1093,7 @@
 
             // Update the time and recompute the fields.
             setTimeInMillis(millis);
+            // Android-changed END: time zone related calculation via helper methods
         }
     }
 
@@ -2344,9 +2346,12 @@
         }
         if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
             if (tz instanceof ZoneInfo) {
-                // Android changed: libcore ZoneInfo uses different method to get offsets.
+                // Android changed BEGIN: use libcore.util.ZoneInfo
+                // The method name to get offsets differs from sun.util.calendar.ZoneInfo
+                // zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
                 ZoneInfo zoneInfo = (ZoneInfo) tz;
                 zoneOffset = zoneInfo.getOffsetsByUtcTime(time, zoneOffsets);
+                // Android-changed END: use libcore.util.ZoneInfo
             } else {
                 zoneOffset = tz.getOffset(time);
                 zoneOffsets[0] = tz.getRawOffset();
@@ -2796,11 +2801,11 @@
         // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
         // or DST_OFFSET fields; then we use those fields.
         TimeZone zone = getZone();
-        // Android changed: move time zone related calculation to separate method.
-
+        // Android-changed BEGIN: time zone related calculation via helper methods
         int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
 
         millis = adjustForZoneAndDaylightSavingsTime(tzMask, millis, zone);
+        // Android-changed END: time zone related calculation via helper methods
 
         // Set this calendar's time in milliseconds
         time = millis;
@@ -2823,6 +2828,7 @@
         setFieldsNormalized(mask);
     }
 
+    // Android-added BEGIN: helper methods for time zone related calculation
     /**
      * Calculates the time in milliseconds that this calendar represents using the UTC time,
      * timezone information (specifically Daylight Savings Time (DST) rules, if any) and knowledge
@@ -2992,6 +2998,7 @@
         }
         return dstOffset;
     }
+    // Android-added END: helper methods for time zone related calculation
 
     /**
      * Computes the fixed date under either the Gregorian or the
diff --git a/ojluni/src/main/java/java/util/Hashtable.java b/ojluni/src/main/java/java/util/Hashtable.java
index 9997b94..4f5394e 100644
--- a/ojluni/src/main/java/java/util/Hashtable.java
+++ b/ojluni/src/main/java/java/util/Hashtable.java
@@ -973,10 +973,10 @@
         HashtableEntry<Object, Object> entryStack = null;
 
         synchronized (this) {
-            // Write out the length, threshold, loadfactor
+            // Write out the threshold and loadFactor
             s.defaultWriteObject();
 
-            // Write out length, count of elements
+            // Write out the length and count of elements
             s.writeInt(table.length);
             s.writeInt(count);
 
@@ -1006,22 +1006,34 @@
     private void readObject(java.io.ObjectInputStream s)
          throws IOException, ClassNotFoundException
     {
-        // Read in the length, threshold, and loadfactor
+        // Read in the threshold and loadFactor
         s.defaultReadObject();
 
+        // Validate loadFactor (ignore threshold - it will be re-computed)
+        if (loadFactor <= 0 || Float.isNaN(loadFactor))
+            throw new StreamCorruptedException("Illegal Load: " + loadFactor);
+
         // Read the original length of the array and number of elements
         int origlength = s.readInt();
         int elements = s.readInt();
 
-        // Compute new size with a bit of room 5% to grow but
-        // no larger than the original size.  Make the length
+        // Validate # of elements
+        if (elements < 0)
+            throw new StreamCorruptedException("Illegal # of Elements: " + elements);
+
+        // Clamp original length to be more than elements / loadFactor
+        // (this is the invariant enforced with auto-growth)
+        origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
+
+        // Compute new length with a bit of room 5% + 3 to grow but
+        // no larger than the clamped original length.  Make the length
         // odd if it's large enough, this helps distribute the entries.
         // Guard against the length ending up zero, that's not valid.
-        int length = (int)(elements * loadFactor) + (elements / 20) + 3;
+        int length = (int)((elements + elements / 20) / loadFactor) + 3;
+
         if (length > elements && (length & 1) == 0)
             length--;
-        if (origlength > 0 && length > origlength)
-            length = origlength;
+        length = Math.min(length, origlength);
         table = new HashtableEntry<?,?>[length];
         threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
         count = 0;
@@ -1032,7 +1044,7 @@
                 K key = (K)s.readObject();
             @SuppressWarnings("unchecked")
                 V value = (V)s.readObject();
-            // synch could be eliminated for performance
+            // sync is eliminated for performance
             reconstitutionPut(table, key, value);
         }
     }
@@ -1044,9 +1056,9 @@
      *
      * <p>This differs from the regular put method in several ways. No
      * checking for rehashing is necessary since the number of elements
-     * initially in the table is known. The modCount is not incremented
-     * because we are creating a new instance. Also, no return value
-     * is needed.
+     * initially in the table is known. The modCount is not incremented and
+     * there's no synchronization because we are creating a new instance.
+     * Also, no return value is needed.
      */
     private void reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value)
         throws StreamCorruptedException
diff --git a/ojluni/src/main/java/java/util/Locale.java b/ojluni/src/main/java/java/util/Locale.java
index 9c75a32..45949c6 100644
--- a/ojluni/src/main/java/java/util/Locale.java
+++ b/ojluni/src/main/java/java/util/Locale.java
@@ -886,7 +886,8 @@
      */
     public static Locale getDefault() {
         // do not synchronize this method - see 4071298
-        return defaultLocale;
+        // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+        return NoImagePreloadHolder.defaultLocale;
     }
 
     /**
@@ -972,6 +973,8 @@
     }
 
     private static Locale initDefault(Locale.Category category) {
+        // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+        final Locale defaultLocale = NoImagePreloadHolder.defaultLocale;
         return getInstance(
             System.getProperty(category.languageKey, defaultLocale.getLanguage()),
             System.getProperty(category.scriptKey, defaultLocale.getScript()),
@@ -1012,7 +1015,8 @@
     public static synchronized void setDefault(Locale newLocale) {
         setDefault(Category.DISPLAY, newLocale);
         setDefault(Category.FORMAT, newLocale);
-        defaultLocale = newLocale;
+        // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+        NoImagePreloadHolder.defaultLocale = newLocale;
         // Android-added: Keep ICU state in sync with java.util.
         ICU.setDefaultLocale(newLocale.toLanguageTag());
     }
@@ -2184,7 +2188,10 @@
      */
     private transient volatile int hashCodeValue = 0;
 
-    private volatile static Locale defaultLocale = initDefault();
+    // Android-changed: Add NoImagePreloadHolder to allow compile-time initialization.
+    private static class NoImagePreloadHolder {
+        public volatile static Locale defaultLocale = initDefault();
+    }
     private volatile static Locale defaultDisplayLocale = null;
     private volatile static Locale defaultFormatLocale = null;
 
diff --git a/ojluni/src/main/java/java/util/Spliterator.java b/ojluni/src/main/java/java/util/Spliterator.java
index 18cb082..35322b3 100644
--- a/ojluni/src/main/java/java/util/Spliterator.java
+++ b/ojluni/src/main/java/java/util/Spliterator.java
@@ -125,7 +125,7 @@
  * are encountered.
  *
  * @apiNote
- * <p>Spliterators, like {@code Iterators}s, are for traversing the elements of
+ * <p>Spliterators, like {@code Iterator}s, are for traversing the elements of
  * a source.  The {@code Spliterator} API was designed to support efficient
  * parallel traversal in addition to sequential traversal, by supporting
  * decomposition as well as single-element iteration.  In addition, the
diff --git a/ojluni/src/main/java/java/util/logging/Level.java b/ojluni/src/main/java/java/util/logging/Level.java
index ce62fd0..afc7035 100644
--- a/ojluni/src/main/java/java/util/logging/Level.java
+++ b/ojluni/src/main/java/java/util/logging/Level.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -602,11 +602,14 @@
             if (list != null) {
                 for (KnownLevel level : list) {
                     Level other = level.mirroredLevel;
+                    Class<? extends Level> type = level.levelObject.getClass();
                     if (l.value == other.value &&
                            (l.resourceBundleName == other.resourceBundleName ||
                                (l.resourceBundleName != null &&
                                 l.resourceBundleName.equals(other.resourceBundleName)))) {
-                        return level;
+                        if (type == l.getClass()) {
+                            return level;
+                        }
                     }
                 }
             }
diff --git a/ojluni/src/main/java/java/util/logging/LogRecord.java b/ojluni/src/main/java/java/util/logging/LogRecord.java
index c555ab3..e5ead8b 100644
--- a/ojluni/src/main/java/java/util/logging/LogRecord.java
+++ b/ojluni/src/main/java/java/util/logging/LogRecord.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -501,13 +501,21 @@
             throw new IOException("LogRecord: bad version: " + major + "." + minor);
         }
         int len = in.readInt();
-        if (len == -1) {
+        if (len < -1) {
+            throw new NegativeArraySizeException();
+        } else if (len == -1) {
             parameters = null;
-        } else {
+        } else if (len < 255) {
             parameters = new Object[len];
             for (int i = 0; i < parameters.length; i++) {
                 parameters[i] = in.readObject();
             }
+        } else {
+            List<Object> params = new ArrayList<>(Math.min(len, 1024));
+            for (int i = 0; i < len; i++) {
+                params.add(in.readObject());
+            }
+            parameters = params.toArray(new Object[params.size()]);
         }
         // If necessary, try to regenerate the resource bundle.
         if (resourceBundleName != null) {
diff --git a/ojluni/src/main/java/java/util/logging/Logger.java b/ojluni/src/main/java/java/util/logging/Logger.java
index c8b76f1..8cf98fe 100644
--- a/ojluni/src/main/java/java/util/logging/Logger.java
+++ b/ojluni/src/main/java/java/util/logging/Logger.java
@@ -805,6 +805,7 @@
      * @param   level   One of the message level identifiers, e.g., SEVERE
      * @param   msgSupplier   A function, which when called, produces the
      *                        desired log message
+     * @since 1.8
      */
     public void log(Level level, Supplier<String> msgSupplier) {
         if (!isLoggable(level)) {
diff --git a/ojluni/src/main/java/sun/misc/Unsafe.java b/ojluni/src/main/java/sun/misc/Unsafe.java
index 90b9f84..c9ba16e 100644
--- a/ojluni/src/main/java/sun/misc/Unsafe.java
+++ b/ojluni/src/main/java/sun/misc/Unsafe.java
@@ -25,6 +25,7 @@
 
 package sun.misc;
 
+import dalvik.annotation.optimization.FastNative;
 import dalvik.system.VMStack;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -110,7 +111,9 @@
       return getArrayIndexScaleForComponentType(component);
     }
 
+    @FastNative
     private static native int getArrayBaseOffsetForComponentType(Class component_class);
+    @FastNative
     private static native int getArrayIndexScaleForComponentType(Class component_class);
 
     /**
@@ -125,6 +128,7 @@
      * @return <code>true</code> if the new value was in fact stored, and
      * <code>false</code> if not
      */
+    @FastNative
     public native boolean compareAndSwapInt(Object obj, long offset,
             int expectedValue, int newValue);
 
@@ -140,6 +144,7 @@
      * @return <code>true</code> if the new value was in fact stored, and
      * <code>false</code> if not
      */
+    @FastNative
     public native boolean compareAndSwapLong(Object obj, long offset,
             long expectedValue, long newValue);
 
@@ -155,6 +160,7 @@
      * @return <code>true</code> if the new value was in fact stored, and
      * <code>false</code> if not
      */
+    @FastNative
     public native boolean compareAndSwapObject(Object obj, long offset,
             Object expectedValue, Object newValue);
 
@@ -166,6 +172,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @return the retrieved value
      */
+    @FastNative
     public native int getIntVolatile(Object obj, long offset);
 
     /**
@@ -176,6 +183,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @param newValue the value to store
      */
+    @FastNative
     public native void putIntVolatile(Object obj, long offset, int newValue);
 
     /**
@@ -186,6 +194,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @return the retrieved value
      */
+    @FastNative
     public native long getLongVolatile(Object obj, long offset);
 
     /**
@@ -196,6 +205,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @param newValue the value to store
      */
+    @FastNative
     public native void putLongVolatile(Object obj, long offset, long newValue);
 
     /**
@@ -206,6 +216,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @return the retrieved value
      */
+    @FastNative
     public native Object getObjectVolatile(Object obj, long offset);
 
     /**
@@ -216,6 +227,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @param newValue the value to store
      */
+    @FastNative
     public native void putObjectVolatile(Object obj, long offset,
             Object newValue);
 
@@ -226,6 +238,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @return the retrieved value
      */
+    @FastNative
     public native int getInt(Object obj, long offset);
 
     /**
@@ -235,11 +248,13 @@
      * @param offset offset to the field within <code>obj</code>
      * @param newValue the value to store
      */
+    @FastNative
     public native void putInt(Object obj, long offset, int newValue);
 
     /**
      * Lazy set an int field.
      */
+    @FastNative
     public native void putOrderedInt(Object obj, long offset, int newValue);
 
     /**
@@ -249,6 +264,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @return the retrieved value
      */
+    @FastNative
     public native long getLong(Object obj, long offset);
 
     /**
@@ -258,11 +274,13 @@
      * @param offset offset to the field within <code>obj</code>
      * @param newValue the value to store
      */
+    @FastNative
     public native void putLong(Object obj, long offset, long newValue);
 
     /**
      * Lazy set a long field.
      */
+    @FastNative
     public native void putOrderedLong(Object obj, long offset, long newValue);
 
     /**
@@ -272,6 +290,7 @@
      * @param offset offset to the field within <code>obj</code>
      * @return the retrieved value
      */
+    @FastNative
     public native Object getObject(Object obj, long offset);
 
     /**
@@ -281,26 +300,40 @@
      * @param offset offset to the field within <code>obj</code>
      * @param newValue the value to store
      */
+    @FastNative
     public native void putObject(Object obj, long offset, Object newValue);
 
     /**
      * Lazy set an object field.
      */
+    @FastNative
     public native void putOrderedObject(Object obj, long offset,
             Object newValue);
 
 
+    @FastNative
     public native boolean getBoolean(Object obj, long offset);
+    @FastNative
     public native void putBoolean(Object obj, long offset, boolean newValue);
+    @FastNative
     public native byte getByte(Object obj, long offset);
+    @FastNative
     public native void putByte(Object obj, long offset, byte newValue);
+    @FastNative
     public native char getChar(Object obj, long offset);
+    @FastNative
     public native void putChar(Object obj, long offset, char newValue);
+    @FastNative
     public native short getShort(Object obj, long offset);
+    @FastNative
     public native void putShort(Object obj, long offset, short newValue);
+    @FastNative
     public native float getFloat(Object obj, long offset);
+    @FastNative
     public native void putFloat(Object obj, long offset, float newValue);
+    @FastNative
     public native double getDouble(Object obj, long offset);
+    @FastNative
     public native void putDouble(Object obj, long offset, double newValue);
 
     /**
@@ -348,50 +381,72 @@
      */
     public native Object allocateInstance(Class<?> c);
 
+    @FastNative
     public native int addressSize();
 
+    @FastNative
     public native int pageSize();
 
+    @FastNative
     public native long allocateMemory(long bytes);
 
+    @FastNative
     public native void freeMemory(long address);
 
+    @FastNative
     public native void setMemory(long address, long bytes, byte value);
 
+    @FastNative
     public native byte getByte(long address);
 
+    @FastNative
     public native void putByte(long address, byte x);
 
+    @FastNative
     public native short getShort(long address);
 
+    @FastNative
     public native void putShort(long address, short x);
 
+    @FastNative
     public native char getChar(long address);
 
+    @FastNative
     public native void putChar(long address, char x);
 
+    @FastNative
     public native int getInt(long address);
 
+    @FastNative
     public native void putInt(long address, int x);
 
+    @FastNative
     public native long getLong(long address);
 
+    @FastNative
     public native void putLong(long address, long x);
 
+    @FastNative
     public native float getFloat(long address);
 
+    @FastNative
     public native void putFloat(long address, float x);
 
+    @FastNative
     public native double getDouble(long address);
 
+    @FastNative
     public native void putDouble(long address, double x);
 
+    @FastNative
     public native void copyMemoryToPrimitiveArray(long srcAddr,
             Object dst, long dstOffset, long bytes);
 
+    @FastNative
     public native void copyMemoryFromPrimitiveArray(Object src, long srcOffset,
             long dstAddr, long bytes);
 
+    @FastNative
     public native void copyMemory(long srcAddr, long dstAddr, long bytes);
 
 
@@ -512,6 +567,7 @@
      * @since 1.8
      */
     // @HotSpotIntrinsicCandidate
+    @FastNative
     public native void loadFence();
 
     /**
@@ -527,6 +583,7 @@
      * @since 1.8
      */
     // @HotSpotIntrinsicCandidate
+    @FastNative
     public native void storeFence();
 
     /**
@@ -539,5 +596,6 @@
      * @since 1.8
      */
     // @HotSpotIntrinsicCandidate
+    @FastNative
     public native void fullFence();
 }
diff --git a/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java b/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java
index c0f8594..08d7375 100644
--- a/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java
+++ b/ojluni/src/main/java/sun/nio/ch/DatagramChannelImpl.java
@@ -1088,7 +1088,10 @@
 
     protected void finalize() throws Throwable {
         try {
-            guard.warnIfOpen();
+            // Android-changed: Add CloseGuard support.
+            if (guard != null) {
+                guard.warnIfOpen();
+            }
             // fd is null if constructor threw exception
             if (fd != null)
                 close();
diff --git a/ojluni/src/main/native/Character.cpp b/ojluni/src/main/native/Character.cpp
index faa27b8..6a82fde 100644
--- a/ojluni/src/main/native/Character.cpp
+++ b/ojluni/src/main/native/Character.cpp
@@ -21,14 +21,12 @@
 #include "jni.h"
 #include "jvm.h"
 #include "JNIHelp.h"
+#include "nativehelper/jni_macros.h"
 #include "unicode/uchar.h"
 #include "unicode/uscript.h"
 #include <math.h>
 #include <stdio.h> // For BUFSIZ
 
-#define NATIVE_METHOD(className, functionName, signature) \
-{ #functionName, signature, (void*)(className ## _ ## functionName) }
-
 JNIEXPORT jboolean JNICALL
 Character_isLowerCaseImpl(JNIEnv* env, jclass, jint codePoint) {
   return u_islower(codePoint);
@@ -161,29 +159,29 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Character, digitImpl, "!(II)I"),
-  NATIVE_METHOD(Character, getDirectionalityImpl, "!(I)B"),
+  FAST_NATIVE_METHOD(Character, digitImpl, "(II)I"),
+  FAST_NATIVE_METHOD(Character, getDirectionalityImpl, "(I)B"),
   NATIVE_METHOD(Character, getNameImpl, "(I)Ljava/lang/String;"),
-  NATIVE_METHOD(Character, getNumericValueImpl, "!(I)I"),
-  NATIVE_METHOD(Character, getTypeImpl, "!(I)I"),
-  NATIVE_METHOD(Character, isAlphabeticImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isDefinedImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isDigitImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isIdentifierIgnorableImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isIdeographicImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isLetterImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isLetterOrDigitImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isLowerCaseImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isMirroredImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isSpaceCharImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isTitleCaseImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isUnicodeIdentifierPartImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isUnicodeIdentifierStartImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isUpperCaseImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, isWhitespaceImpl, "!(I)Z"),
-  NATIVE_METHOD(Character, toLowerCaseImpl, "!(I)I"),
-  NATIVE_METHOD(Character, toTitleCaseImpl, "!(I)I"),
-  NATIVE_METHOD(Character, toUpperCaseImpl, "!(I)I"),
+  FAST_NATIVE_METHOD(Character, getNumericValueImpl, "(I)I"),
+  FAST_NATIVE_METHOD(Character, getTypeImpl, "(I)I"),
+  FAST_NATIVE_METHOD(Character, isAlphabeticImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isDefinedImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isDigitImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isIdentifierIgnorableImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isIdeographicImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isLetterImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isLetterOrDigitImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isLowerCaseImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isMirroredImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isSpaceCharImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isTitleCaseImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isUnicodeIdentifierPartImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isUnicodeIdentifierStartImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isUpperCaseImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, isWhitespaceImpl, "(I)Z"),
+  FAST_NATIVE_METHOD(Character, toLowerCaseImpl, "(I)I"),
+  FAST_NATIVE_METHOD(Character, toTitleCaseImpl, "(I)I"),
+  FAST_NATIVE_METHOD(Character, toUpperCaseImpl, "(I)I"),
 };
 
 void register_java_lang_Character(JNIEnv* env) {
diff --git a/ojluni/src/main/native/Math.c b/ojluni/src/main/native/Math.c
index 7bc076c..c5482ba 100644
--- a/ojluni/src/main/native/Math.c
+++ b/ojluni/src/main/native/Math.c
@@ -20,13 +20,11 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include "nativehelper/jni_macros.h"
 
 #include <stdlib.h>
 #include <math.h>
 
-#define NATIVE_METHOD(className, functionName, signature) \
-{ #functionName, signature, (void*)(className ## _ ## functionName) }
-
 JNIEXPORT jdouble JNICALL
 Math_cos(JNIEnv *env, jclass unused, jdouble d) {
     return cos(d);
@@ -145,29 +143,29 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Math, IEEEremainder, "!(DD)D"),
-  NATIVE_METHOD(Math, acos, "!(D)D"),
-  NATIVE_METHOD(Math, asin, "!(D)D"),
-  NATIVE_METHOD(Math, atan, "!(D)D"),
-  NATIVE_METHOD(Math, atan2, "!(DD)D"),
-  NATIVE_METHOD(Math, cbrt, "!(D)D"),
-  NATIVE_METHOD(Math, cos, "!(D)D"),
-  NATIVE_METHOD(Math, ceil, "!(D)D"),
-  NATIVE_METHOD(Math, cosh, "!(D)D"),
-  NATIVE_METHOD(Math, exp, "!(D)D"),
-  NATIVE_METHOD(Math, expm1, "!(D)D"),
-  NATIVE_METHOD(Math, floor, "!(D)D"),
-  NATIVE_METHOD(Math, hypot, "!(DD)D"),
-  NATIVE_METHOD(Math, log, "!(D)D"),
-  NATIVE_METHOD(Math, log10, "!(D)D"),
-  NATIVE_METHOD(Math, log1p, "!(D)D"),
-  NATIVE_METHOD(Math, pow, "!(DD)D"),
-  NATIVE_METHOD(Math, rint, "!(D)D"),
-  NATIVE_METHOD(Math, sin, "!(D)D"),
-  NATIVE_METHOD(Math, sinh, "!(D)D"),
-  NATIVE_METHOD(Math, sqrt, "!(D)D"),
-  NATIVE_METHOD(Math, tan, "!(D)D"),
-  NATIVE_METHOD(Math, tanh, "!(D)D"),
+  FAST_NATIVE_METHOD(Math, IEEEremainder, "(DD)D"),
+  FAST_NATIVE_METHOD(Math, acos, "(D)D"),
+  FAST_NATIVE_METHOD(Math, asin, "(D)D"),
+  FAST_NATIVE_METHOD(Math, atan, "(D)D"),
+  FAST_NATIVE_METHOD(Math, atan2, "(DD)D"),
+  FAST_NATIVE_METHOD(Math, cbrt, "(D)D"),
+  FAST_NATIVE_METHOD(Math, cos, "(D)D"),
+  FAST_NATIVE_METHOD(Math, ceil, "(D)D"),
+  FAST_NATIVE_METHOD(Math, cosh, "(D)D"),
+  FAST_NATIVE_METHOD(Math, exp, "(D)D"),
+  FAST_NATIVE_METHOD(Math, expm1, "(D)D"),
+  FAST_NATIVE_METHOD(Math, floor, "(D)D"),
+  FAST_NATIVE_METHOD(Math, hypot, "(DD)D"),
+  FAST_NATIVE_METHOD(Math, log, "(D)D"),
+  FAST_NATIVE_METHOD(Math, log10, "(D)D"),
+  FAST_NATIVE_METHOD(Math, log1p, "(D)D"),
+  FAST_NATIVE_METHOD(Math, pow, "(DD)D"),
+  FAST_NATIVE_METHOD(Math, rint, "(D)D"),
+  FAST_NATIVE_METHOD(Math, sin, "(D)D"),
+  FAST_NATIVE_METHOD(Math, sinh, "(D)D"),
+  FAST_NATIVE_METHOD(Math, sqrt, "(D)D"),
+  FAST_NATIVE_METHOD(Math, tan, "(D)D"),
+  FAST_NATIVE_METHOD(Math, tanh, "(D)D"),
 };
 
 void register_java_lang_Math(JNIEnv* env) {
diff --git a/ojluni/src/main/native/Runtime.c b/ojluni/src/main/native/Runtime.c
index 90a8898..61de356 100644
--- a/ojluni/src/main/native/Runtime.c
+++ b/ojluni/src/main/native/Runtime.c
@@ -41,8 +41,7 @@
 
 #include "JNIHelp.h"
 
-#define NATIVE_METHOD(className, functionName, signature) \
-{ #functionName, signature, (void*)(className ## _ ## functionName) }
+#include "nativehelper/jni_macros.h"
 
 JNIEXPORT jlong JNICALL
 Runtime_freeMemory(JNIEnv *env, jobject this)
@@ -82,9 +81,9 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Runtime, freeMemory, "!()J"),
-  NATIVE_METHOD(Runtime, totalMemory, "!()J"),
-  NATIVE_METHOD(Runtime, maxMemory, "!()J"),
+  FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
+  FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
+  FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
   NATIVE_METHOD(Runtime, gc, "()V"),
   NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
   NATIVE_METHOD(Runtime, nativeLoad,
diff --git a/ojluni/src/main/native/openjdksub.mk b/ojluni/src/main/native/openjdksub.mk
index 9e7dc07..3d37d00 100644
--- a/ojluni/src/main/native/openjdksub.mk
+++ b/ojluni/src/main/native/openjdksub.mk
@@ -70,6 +70,5 @@
 LOCAL_C_INCLUDES += \
        libcore/$(srcdir) \
        external/fdlibm \
-       external/openssl/include \
        external/zlib \
        external/icu/icu4c/source/common \
diff --git a/ojluni/src/test/java/nio/file/TestUtil.java b/ojluni/src/test/java/nio/file/TestUtil.java
new file mode 100644
index 0000000..2d102fd
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/TestUtil.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+// Android-changed: Adapted from jdk/test/java/nio/file/TestUtil.java
+// Android-changed: Added package & made methods public
+package test.java.nio.file;
+
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Random;
+import java.io.IOException;
+
+public class TestUtil {
+    private TestUtil() {
+    }
+
+    static public Path createTemporaryDirectory(String where) throws IOException {
+        Path dir = FileSystems.getDefault().getPath(where);
+        return Files.createTempDirectory(dir, "name");
+    }
+
+    static public Path createTemporaryDirectory() throws IOException {
+        return Files.createTempDirectory("name");
+    }
+
+    static public void removeAll(Path dir) throws IOException {
+        Files.walkFileTree(dir, new FileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+                return FileVisitResult.CONTINUE;
+            }
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+                try {
+                    Files.delete(file);
+                } catch (IOException x) {
+                    System.err.format("Unable to delete %s: %s\n", file, x);
+                }
+                return FileVisitResult.CONTINUE;
+            }
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+                try {
+                    Files.delete(dir);
+                } catch (IOException x) {
+                    System.err.format("Unable to delete %s: %s\n", dir, x);
+                }
+                return FileVisitResult.CONTINUE;
+            }
+            @Override
+            public FileVisitResult visitFileFailed(Path file, IOException exc) {
+                System.err.format("Unable to visit %s: %s\n", file, exc);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    static public void deleteUnchecked(Path file) {
+        try {
+            Files.delete(file);
+        } catch (IOException exc) {
+            System.err.format("Unable to delete %s: %s\n", file, exc);
+        }
+    }
+
+    /**
+     * Creates a directory tree in the given directory so that the total
+     * size of the path is more than 2k in size. This is used for long
+     * path tests on Windows.
+     */
+    static public Path createDirectoryWithLongPath(Path dir)
+        throws IOException
+    {
+        StringBuilder sb = new StringBuilder();
+        for (int i=0; i<240; i++) {
+            sb.append('A');
+        }
+        String name = sb.toString();
+        do {
+            dir = dir.resolve(name).resolve(".");
+            Files.createDirectory(dir);
+        } while (dir.toString().length() < 2048);
+        return dir;
+    }
+
+    /**
+     * Returns true if symbolic links are supported
+     */
+    static public boolean supportsLinks(Path dir) {
+        Path link = dir.resolve("testlink");
+        Path target = dir.resolve("testtarget");
+        try {
+            Files.createSymbolicLink(link, target);
+            Files.delete(link);
+            return true;
+        } catch (UnsupportedOperationException x) {
+            return false;
+        } catch (IOException x) {
+            return false;
+        }
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java b/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java
new file mode 100644
index 0000000..7f0c17b
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/AclEntryEmptySetTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 7076310
+ * @summary Test AclEntry.Builder setFlags and setPermissions with empty sets
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/AclEntry/EmptySet.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+
+/*
+ * Test for bug 7076310 "(file) AclEntry.Builder setFlags throws
+ * IllegalArgumentException if set argument is empty"
+ * The bug is only applies when the given Set is NOT an instance of EnumSet.
+ *
+ * The setPermissions method also has the same problem.
+ */
+// Android-changed: Renamed from "EmptySet"
+public class AclEntryEmptySetTest {
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        Set<AclEntryFlag> flags = new HashSet<>();
+        AclEntry.newBuilder().setFlags(flags);
+
+        Set<AclEntryPermission> perms = new HashSet<>();
+        AclEntry.newBuilder().setPermissions(perms);
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
new file mode 100644
index 0000000..2d9e32c
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/AclFileAttributeViewTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333 6891404
+ * @summary Unit test for java.nio.file.attribute.AclFileAttribueView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/AclFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+import static java.nio.file.attribute.AclEntryType.*;
+import static java.nio.file.attribute.AclEntryPermission.*;
+import static java.nio.file.attribute.AclEntryFlag.*;
+
+// Android-changed: Renamed from "Basic"
+public class AclFileAttributeViewTest {
+
+    static void printAcl(List<AclEntry> acl) {
+        for (AclEntry entry: acl) {
+            System.out.format("  %s%n", entry);
+        }
+    }
+
+    // sanity check read and writing ACL
+    static void testReadWrite(Path dir) throws IOException {
+        Path file = dir.resolve("foo");
+        if (Files.notExists(file))
+            Files.createFile(file);
+
+        AclFileAttributeView view =
+            Files.getFileAttributeView(file, AclFileAttributeView.class);
+
+        // print existing ACL
+        List<AclEntry> acl = view.getAcl();
+        System.out.println(" -- current ACL --");
+        printAcl(acl);
+
+        // insert entry to grant owner read access
+        UserPrincipal owner = view.getOwner();
+        AclEntry entry = AclEntry.newBuilder()
+            .setType(ALLOW)
+            .setPrincipal(owner)
+            .setPermissions(READ_DATA, READ_ATTRIBUTES)
+            .build();
+        System.out.println(" -- insert (entry 0) --");
+        System.out.format("  %s%n", entry);
+        acl.add(0, entry);
+        view.setAcl(acl);
+
+        // re-ACL and check entry
+        List<AclEntry> newacl = view.getAcl();
+        System.out.println(" -- current ACL --");
+        printAcl(acl);
+        if (!newacl.get(0).equals(entry)) {
+            throw new RuntimeException("Entry 0 is not expected");
+        }
+
+        // if PosixFileAttributeView then repeat test with OWNER@
+        if (Files.getFileStore(file).supportsFileAttributeView("posix")) {
+            owner = file.getFileSystem().getUserPrincipalLookupService()
+                .lookupPrincipalByName("OWNER@");
+            entry = AclEntry.newBuilder(entry).setPrincipal(owner).build();
+
+            System.out.println(" -- replace (entry 0) --");
+            System.out.format("  %s%n", entry);
+
+            acl.set(0, entry);
+            view.setAcl(acl);
+            newacl = view.getAcl();
+            System.out.println(" -- current ACL --");
+            printAcl(acl);
+            if (!newacl.get(0).equals(entry)) {
+                throw new RuntimeException("Entry 0 is not expected");
+            }
+        }
+    }
+
+    static FileAttribute<List<AclEntry>> asAclAttribute(final List<AclEntry> acl) {
+        return new FileAttribute<List<AclEntry>>() {
+            public String name() { return "acl:acl"; }
+            public List<AclEntry> value() { return acl; }
+        };
+    }
+
+    static void assertEquals(List<AclEntry> actual, List<AclEntry> expected) {
+        if (!actual.equals(expected)) {
+            System.err.format("Actual: %s\n", actual);
+            System.err.format("Expected: %s\n", expected);
+            throw new RuntimeException("ACL not expected");
+        }
+    }
+
+    // sanity check create a file or directory with initial ACL
+    static void testCreateFile(Path dir) throws IOException {
+        UserPrincipal user = Files.getOwner(dir);
+        AclFileAttributeView view;
+
+        // create file with initial ACL
+        System.out.println("-- create file with initial ACL --");
+        Path file = dir.resolve("gus");
+        List<AclEntry> fileAcl = Arrays.asList(
+            AclEntry.newBuilder()
+                .setType(AclEntryType.ALLOW)
+                .setPrincipal(user)
+                .setPermissions(SYNCHRONIZE, READ_DATA, WRITE_DATA,
+                    READ_ATTRIBUTES, READ_ACL, WRITE_ATTRIBUTES, DELETE)
+                .build());
+        Files.createFile(file, asAclAttribute(fileAcl));
+        view = Files.getFileAttributeView(file, AclFileAttributeView.class);
+        assertEquals(view.getAcl(), fileAcl);
+
+        // create directory with initial ACL
+        System.out.println("-- create directory with initial ACL --");
+        Path subdir = dir.resolve("stuff");
+        List<AclEntry> dirAcl = Arrays.asList(
+            AclEntry.newBuilder()
+                .setType(AclEntryType.ALLOW)
+                .setPrincipal(user)
+                .setPermissions(SYNCHRONIZE, ADD_FILE, DELETE)
+                .build(),
+            AclEntry.newBuilder(fileAcl.get(0))
+                .setFlags(FILE_INHERIT)
+                .build());
+        Files.createDirectory(subdir, asAclAttribute(dirAcl));
+        view = Files.getFileAttributeView(subdir, AclFileAttributeView.class);
+        assertEquals(view.getAcl(), dirAcl);
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() throws IOException {
+        // use work directory rather than system temporary directory to
+        // improve chances that ACLs are supported
+        // Android-changed: Switched to temp dir due to permissions
+        // Path dir = Paths.get("./work" + new Random().nextInt());
+        // Files.createTempDirectory(dir);
+        Path dir = Files.createTempDirectory("acl");
+        try {
+            if (!Files.getFileStore(dir).supportsFileAttributeView("acl")) {
+                System.out.println("ACLs not supported - test skipped!");
+                return;
+            }
+            testReadWrite(dir);
+
+            // only currently feasible on Windows
+            if (System.getProperty("os.name").startsWith("Windows"))
+                testCreateFile(dir);
+
+        } finally {
+            TestUtil.removeAll(dir);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
new file mode 100644
index 0000000..b393981
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewCreationTimeTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8011536
+ * @summary Basic test for creationTime attribute on platforms/file systems
+ *     that support it.
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.Path;
+import java.nio.file.Files;
+import java.nio.file.attribute.*;
+import java.time.Instant;
+import java.io.IOException;
+
+// Android-changed: Renamed from "CreationTime"
+public class BasicFileAttributeViewCreationTimeTest {
+
+    private static final java.io.PrintStream err = System.err;
+
+    /**
+     * Reads the creationTime attribute
+     */
+    private static FileTime creationTime(Path file) throws IOException {
+        return Files.readAttributes(file, BasicFileAttributes.class).creationTime();
+    }
+
+    /**
+     * Sets the creationTime attribute
+     */
+    private static void setCreationTime(Path file, FileTime time) throws IOException {
+        BasicFileAttributeView view =
+            Files.getFileAttributeView(file, BasicFileAttributeView.class);
+        view.setTimes(null, null, time);
+    }
+
+    static void test(Path top) throws IOException {
+        Path file = Files.createFile(top.resolve("foo"));
+
+        /**
+         * Check that creationTime reported
+         */
+        FileTime creationTime = creationTime(file);
+        Instant now = Instant.now();
+        if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) {
+            err.println("File creation time reported as: " + creationTime);
+            throw new RuntimeException("Expected to be close to: " + now);
+        }
+
+        /**
+         * Is the creationTime attribute supported here?
+         */
+        boolean supportsCreationTimeRead = false;
+        boolean supportsCreationTimeWrite = false;
+        String os = System.getProperty("os.name");
+        if (os.contains("OS X") && Files.getFileStore(file).type().equals("hfs")) {
+            supportsCreationTimeRead = true;
+        } else if (os.startsWith("Windows")) {
+            String type = Files.getFileStore(file).type();
+            if (type.equals("NTFS") || type.equals("FAT")) {
+                supportsCreationTimeRead = true;
+                supportsCreationTimeWrite = true;
+            }
+        }
+
+        /**
+         * If the creation-time attribute is supported then change the file's
+         * last modified and check that it doesn't change the creation-time.
+         */
+        if (supportsCreationTimeRead) {
+            // change modified time by +1 hour
+            Instant plusHour = Instant.now().plusSeconds(60L * 60L);
+            Files.setLastModifiedTime(file, FileTime.from(plusHour));
+            FileTime current = creationTime(file);
+            if (!current.equals(creationTime))
+                throw new RuntimeException("Creation time should not have changed");
+        }
+
+        /**
+         * If the creation-time attribute is supported and can be changed then
+         * check that the change is effective.
+         */
+        if (supportsCreationTimeWrite) {
+            // change creation time by -1 hour
+            Instant minusHour = Instant.now().minusSeconds(60L * 60L);
+            creationTime = FileTime.from(minusHour);
+            setCreationTime(file, creationTime);
+            FileTime current = creationTime(file);
+            if (Math.abs(creationTime.toMillis()-current.toMillis()) > 1000L)
+                throw new RuntimeException("Creation time not changed");
+        }
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() throws IOException {
+        // create temporary directory to run tests
+        Path dir = TestUtil.createTemporaryDirectory();
+        try {
+            test(dir);
+        } finally {
+            TestUtil.removeAll(dir);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java
new file mode 100644
index 0000000..82f4e0e
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/BasicFileAttributeViewTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.BasicFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.io.*;
+
+// Android-changed: Renamed from "Basic"
+public class BasicFileAttributeViewTest {
+
+    static void check(boolean okay, String msg) {
+        if (!okay)
+            throw new RuntimeException(msg);
+    }
+
+    static void checkAttributesOfDirectory(Path dir)
+        throws IOException
+    {
+        BasicFileAttributes attrs = Files.readAttributes(dir, BasicFileAttributes.class);
+        check(attrs.isDirectory(), "is a directory");
+        check(!attrs.isRegularFile(), "is not a regular file");
+        check(!attrs.isSymbolicLink(), "is not a link");
+        check(!attrs.isOther(), "is not other");
+
+        // last-modified-time should match java.io.File in seconds
+        File f = new File(dir.toString());
+        check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS),
+              "last-modified time should be the same");
+    }
+
+    static void checkAttributesOfFile(Path dir, Path file)
+        throws IOException
+    {
+        BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
+        check(attrs.isRegularFile(), "is a regular file");
+        check(!attrs.isDirectory(), "is not a directory");
+        check(!attrs.isSymbolicLink(), "is not a link");
+        check(!attrs.isOther(), "is not other");
+
+        // size and last-modified-time should match java.io.File in seconds
+        File f = new File(file.toString());
+        check(f.length() == attrs.size(), "size should be the same");
+        check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS),
+              "last-modified time should be the same");
+
+        // copy last-modified time from directory to file,
+        // re-read attribtues, and check they match
+        BasicFileAttributeView view =
+            Files.getFileAttributeView(file, BasicFileAttributeView.class);
+        BasicFileAttributes dirAttrs = Files.readAttributes(dir, BasicFileAttributes.class);
+        view.setTimes(dirAttrs.lastModifiedTime(), null, null);
+
+        attrs = view.readAttributes();
+        check(attrs.lastModifiedTime().equals(dirAttrs.lastModifiedTime()),
+            "last-modified time should be equal");
+
+        // security tests
+        check (!(attrs instanceof PosixFileAttributes),
+            "should not be able to cast to PosixFileAttributes");
+    }
+
+    static void checkAttributesOfLink(Path link)
+        throws IOException
+    {
+        BasicFileAttributes attrs =
+            Files.readAttributes(link, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+        check(attrs.isSymbolicLink(), "is a link");
+        check(!attrs.isDirectory(), "is a directory");
+        check(!attrs.isRegularFile(), "is not a regular file");
+        check(!attrs.isOther(), "is not other");
+    }
+
+    static void attributeReadWriteTests(Path dir)
+        throws IOException
+    {
+        // create file
+        Path file = dir.resolve("foo");
+        try (OutputStream out = Files.newOutputStream(file)) {
+            out.write("this is not an empty file".getBytes("UTF-8"));
+        }
+
+        // check attributes of directory and file
+        checkAttributesOfDirectory(dir);
+        checkAttributesOfFile(dir, file);
+
+        // symbolic links may be supported
+        Path link = dir.resolve("link");
+        try {
+            Files.createSymbolicLink(link, file);
+        } catch (UnsupportedOperationException x) {
+            return;
+        } catch (IOException x) {
+            return;
+        }
+        checkAttributesOfLink(link);
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() throws IOException {
+        // create temporary directory to run tests
+        Path dir = TestUtil.createTemporaryDirectory();
+        try {
+            attributeReadWriteTests(dir);
+        } finally {
+            TestUtil.removeAll(dir);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
new file mode 100644
index 0000000..8386a31
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/DosFileAttributeViewTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.DosFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/DosFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+// Android-changed: Renamed from "Basic"
+public class DosFileAttributeViewTest {
+
+    static void check(boolean okay) {
+        if (!okay)
+            throw new RuntimeException("Test failed");
+    }
+
+    // exercise each setter/getter method, leaving all attributes unset
+    static void testAttributes(DosFileAttributeView view) throws IOException {
+        view.setReadOnly(true);
+        check(view.readAttributes().isReadOnly());
+        view.setReadOnly(false);
+        check(!view.readAttributes().isReadOnly());
+        view.setHidden(true);
+        check(view.readAttributes().isHidden());
+        view.setHidden(false);
+        check(!view.readAttributes().isHidden());
+        view.setArchive(true);
+        check(view.readAttributes().isArchive());
+        view.setArchive(false);
+        check(!view.readAttributes().isArchive());
+        view.setSystem(true);
+        check(view.readAttributes().isSystem());
+        view.setSystem(false);
+        check(!view.readAttributes().isSystem());
+    }
+
+    // set the value of all attributes
+    static void setAll(DosFileAttributeView view, boolean value)
+        throws IOException
+    {
+        view.setReadOnly(value);
+        view.setHidden(value);
+        view.setArchive(value);
+        view.setSystem(value);
+    }
+
+    // read and write FAT attributes
+    static void readWriteTests(Path dir) throws IOException {
+
+        // create "foo" and test that we can read/write each FAT attribute
+        Path file = Files.createFile(dir.resolve("foo"));
+        try {
+            testAttributes(Files.getFileAttributeView(file, DosFileAttributeView.class));
+
+            // Following tests use a symbolic link so skip if not supported
+            if (!TestUtil.supportsLinks(dir))
+                return;
+
+            Path link = dir.resolve("link");
+            Files.createSymbolicLink(link, file);
+
+            // test following links
+            testAttributes(Files.getFileAttributeView(link, DosFileAttributeView.class));
+
+            // test not following links
+            try {
+                try {
+                    testAttributes(Files
+                        .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
+                } catch (IOException x) {
+                    // access to link attributes not supported
+                    return;
+                }
+
+                // set all attributes on link
+                // run test on target of link (which leaves them all un-set)
+                // check that attributes of link remain all set
+                setAll(Files
+                    .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), true);
+                testAttributes(Files
+                    .getFileAttributeView(link, DosFileAttributeView.class));
+                DosFileAttributes attrs =
+                    Files.getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS)
+                         .readAttributes();
+                check(attrs.isReadOnly());
+                check(attrs.isHidden());
+                check(attrs.isArchive());
+                check(attrs.isSystem());
+                setAll(Files
+                    .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS), false);
+
+                // set all attributes on target
+                // run test on link (which leaves them all un-set)
+                // check that attributes of target remain all set
+                setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), true);
+                testAttributes(Files
+                    .getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS));
+                attrs = Files.getFileAttributeView(link, DosFileAttributeView.class).readAttributes();
+                check(attrs.isReadOnly());
+                check(attrs.isHidden());
+                check(attrs.isArchive());
+                check(attrs.isSystem());
+                setAll(Files.getFileAttributeView(link, DosFileAttributeView.class), false);
+            } finally {
+                TestUtil.deleteUnchecked(link);
+            }
+        } finally {
+            TestUtil.deleteUnchecked(file);
+        }
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() throws IOException {
+        // create temporary directory to run tests
+        Path dir = TestUtil.createTemporaryDirectory();
+
+        try {
+            // skip test if DOS file attributes not supported
+            if (!Files.getFileStore(dir).supportsFileAttributeView("dos")) {
+                System.out.println("DOS file attribute not supported.");
+                return;
+            }
+            readWriteTests(dir);
+        } finally {
+            TestUtil.removeAll(dir);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java b/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java
new file mode 100644
index 0000000..43c5d61
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/FileTimeTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 6844313 8011647
+ * @summary Unit test for java.nio.file.FileTime
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/FileTime/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.*;
+import java.util.Random;
+import java.util.EnumSet;
+
+// Android-changed: Renamed from "Basic"
+public class FileTimeTest {
+
+    static final Random rand = new Random();
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        long now = System.currentTimeMillis();
+        long tomorrowInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) + 1;
+        long yesterdayInDays = TimeUnit.DAYS.convert(now, MILLISECONDS) - 1;
+
+        Instant nowInstant = Instant.ofEpochMilli(now);
+
+        // equals
+        eq(now, MILLISECONDS, now, MILLISECONDS);
+        eq(now, MILLISECONDS, now*1000L, MICROSECONDS);
+        neq(now, MILLISECONDS, 0, MILLISECONDS);
+        neq(now, MILLISECONDS, 0, MICROSECONDS);
+
+        eq(nowInstant, now, MILLISECONDS);
+        eq(nowInstant, now*1000L, MICROSECONDS);
+        neq(nowInstant, 0, MILLISECONDS);
+        neq(nowInstant, 0, MICROSECONDS);
+
+        // compareTo
+        cmp(now, MILLISECONDS, now, MILLISECONDS, 0);
+        cmp(now, MILLISECONDS, now*1000L, MICROSECONDS, 0);
+        cmp(now, MILLISECONDS, now-1234, MILLISECONDS, 1);
+        cmp(now, MILLISECONDS, now+1234, MILLISECONDS, -1);
+
+        cmp(tomorrowInDays, DAYS, now, MILLISECONDS, 1);
+        cmp(now, MILLISECONDS, tomorrowInDays, DAYS, -1);
+        cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
+        cmp(now, MILLISECONDS, yesterdayInDays, DAYS, 1);
+        cmp(yesterdayInDays, DAYS, now, MILLISECONDS, -1);
+
+        cmp(Long.MAX_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, 1);
+        cmp(Long.MAX_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, 1);
+        cmp(Long.MIN_VALUE, DAYS, Long.MIN_VALUE, NANOSECONDS, -1);
+        cmp(Long.MIN_VALUE, DAYS, Long.MAX_VALUE, NANOSECONDS, -1);
+
+        cmp(Instant.MIN, Long.MIN_VALUE, DAYS, 1);
+        cmp(Instant.MIN, Long.MIN_VALUE, HOURS, 1);
+        cmp(Instant.MIN, Long.MIN_VALUE, MINUTES, 1);
+        cmp(Instant.MIN, Long.MIN_VALUE, SECONDS, 1);
+        cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 1, SECONDS, 1);
+        cmp(Instant.MIN, Instant.MIN.getEpochSecond() - 100, SECONDS, 1);
+        cmp(Instant.MIN, Instant.MIN.getEpochSecond(), SECONDS, 0);
+
+        cmp(Instant.MAX, Long.MAX_VALUE, DAYS, -1);
+        cmp(Instant.MAX, Long.MAX_VALUE, HOURS, -1);
+        cmp(Instant.MAX, Long.MAX_VALUE, MINUTES, -1);
+        cmp(Instant.MAX, Long.MAX_VALUE, SECONDS, -1);
+        cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 1, SECONDS, -1);
+        cmp(Instant.MAX, Instant.MAX.getEpochSecond() + 100, SECONDS, -1);
+        cmp(Instant.MAX, Instant.MAX.getEpochSecond(), SECONDS, 0);
+
+        cmp(nowInstant, now, MILLISECONDS, 0);
+        cmp(nowInstant, now*1000L, MICROSECONDS, 0);
+        cmp(nowInstant, now-1234, MILLISECONDS, 1);
+        cmp(nowInstant, now+1234, MILLISECONDS, -1);
+        cmp(nowInstant, tomorrowInDays, DAYS, -1);
+        cmp(nowInstant, yesterdayInDays, DAYS, 1);
+
+        // to(TimeUnit)
+        to(MILLISECONDS.convert(1, DAYS) - 1, MILLISECONDS);
+        to(MILLISECONDS.convert(1, DAYS) + 0, MILLISECONDS);
+        to(MILLISECONDS.convert(1, DAYS) + 1, MILLISECONDS);
+        to(1, MILLISECONDS);
+        to(0, MILLISECONDS);
+        to(1, MILLISECONDS);
+        to(MILLISECONDS.convert(-1, DAYS) - 1, MILLISECONDS);
+        to(MILLISECONDS.convert(-1, DAYS) + 0, MILLISECONDS);
+        to(MILLISECONDS.convert(-1, DAYS) + 1, MILLISECONDS);
+        for (TimeUnit unit: TimeUnit.values()) {
+            for (int i=0; i<100; i++) { to(rand.nextLong(), unit); }
+            to(Long.MIN_VALUE, unit);
+            to(Long.MAX_VALUE, unit);
+        }
+
+        // toInstant()
+        int N = 1000;
+        for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
+            for (int i = 0; i < N; i++) {
+                long value = rand.nextLong();
+                FileTime ft = FileTime.from(value, unit);
+                Instant instant = ft.toInstant();
+                if (instant != Instant.MIN && instant != Instant.MAX) {
+                    eqTime(value, unit, instant);
+                }
+            }
+        }
+        for (TimeUnit unit : EnumSet.allOf(TimeUnit.class)) {
+            long value = Long.MIN_VALUE;
+            FileTime ft = FileTime.from(value, unit);
+            Instant instant = ft.toInstant();
+            if (unit.compareTo(TimeUnit.SECONDS) < 0) {
+                eqTime(value, unit, instant);
+            } else if (!instant.equals(Instant.MIN)) {
+                throw new RuntimeException("should overflow to MIN");
+            }
+            value = Long.MAX_VALUE;
+            ft = FileTime.from(value, unit);
+            instant = ft.toInstant();
+            if (unit.compareTo(TimeUnit.SECONDS) < 0) {
+                eqTime(value, unit, instant);
+            } else if (!instant.equals(Instant.MAX)) {
+                throw new RuntimeException("should overflow to MAX");
+            }
+        }
+
+        // from(Instant)
+        final long MAX_SECOND = 31556889864403199L;
+        for (int i = 0; i < N; i++) {
+            long v = rand.nextLong();
+            long secs = v % MAX_SECOND;
+            Instant instant = Instant.ofEpochSecond(secs, rand.nextInt(1000_000_000));
+            FileTime ft = FileTime.from(instant);
+            if (!ft.toInstant().equals(instant) || ft.to(SECONDS)  != secs) {
+                throw new RuntimeException("from(Instant) failed");
+            }
+            long millis = v;
+            instant = Instant.ofEpochMilli(millis);
+            ft = FileTime.from(instant);
+            if (!ft.toInstant().equals(instant) ||
+                ft.toMillis()  != instant.toEpochMilli()) {
+                throw new RuntimeException("from(Instant) failed");
+            }
+            long nanos = v;
+            ft = FileTime.from(nanos, NANOSECONDS);
+            secs = nanos / 1000_000_000;
+            nanos = nanos % 1000_000_000;
+            instant = Instant.ofEpochSecond(secs, nanos);
+            if (!ft.equals(FileTime.from(instant))) {
+                throw new RuntimeException("from(Instant) failed");
+            }
+        }
+
+        // toString
+        ts(1L, DAYS, "1970-01-02T00:00:00Z");
+        ts(1L, HOURS, "1970-01-01T01:00:00Z");
+        ts(1L, MINUTES, "1970-01-01T00:01:00Z");
+        ts(1L, SECONDS, "1970-01-01T00:00:01Z");
+        ts(1L, MILLISECONDS, "1970-01-01T00:00:00.001Z");
+        ts(1L, MICROSECONDS, "1970-01-01T00:00:00.000001Z");
+        ts(1L, NANOSECONDS, "1970-01-01T00:00:00.000000001Z");
+        ts(999999999L, NANOSECONDS, "1970-01-01T00:00:00.999999999Z");
+        ts(9999999999L, NANOSECONDS, "1970-01-01T00:00:09.999999999Z");
+
+        ts(-1L, DAYS, "1969-12-31T00:00:00Z");
+        ts(-1L, HOURS, "1969-12-31T23:00:00Z");
+        ts(-1L, MINUTES, "1969-12-31T23:59:00Z");
+        ts(-1L, SECONDS, "1969-12-31T23:59:59Z");
+        ts(-1L, MILLISECONDS, "1969-12-31T23:59:59.999Z");
+        ts(-1L, MICROSECONDS, "1969-12-31T23:59:59.999999Z");
+        ts(-1L, NANOSECONDS, "1969-12-31T23:59:59.999999999Z");
+        ts(-999999999L, NANOSECONDS, "1969-12-31T23:59:59.000000001Z");
+        ts(-9999999999L, NANOSECONDS, "1969-12-31T23:59:50.000000001Z");
+
+        ts(-62135596799999L, MILLISECONDS, "0001-01-01T00:00:00.001Z");
+        ts(-62135596800000L, MILLISECONDS, "0001-01-01T00:00:00Z");
+        ts(-62135596800001L, MILLISECONDS, "-0001-12-31T23:59:59.999Z");
+
+        ts(253402300799999L, MILLISECONDS, "9999-12-31T23:59:59.999Z");
+        ts(-377642044800001L, MILLISECONDS, "-9999-12-31T23:59:59.999Z");
+
+        // NTFS epoch in usec.
+        ts(-11644473600000000L, MICROSECONDS, "1601-01-01T00:00:00Z");
+
+        ts(Instant.MIN, "-1000000001-01-01T00:00:00Z");
+        ts(Instant.MAX, "1000000000-12-31T23:59:59.999999999Z");
+
+        try {
+            FileTime.from(0L, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) { }
+        try {
+            FileTime.from(null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) { }
+
+        FileTime time = FileTime.fromMillis(now);
+        if (time.equals(null))
+            throw new RuntimeException("should not be equal to null");
+        try {
+            time.compareTo(null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) { }
+
+        // Instant + toMilli() overflow
+        overflow(Long.MAX_VALUE,
+                 FileTime.from(Instant.MAX).toMillis());
+        overflow(Long.MAX_VALUE,
+                 FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
+                         .toMillis());
+        overflow(Long.MIN_VALUE,
+                 FileTime.from(Instant.MIN).toMillis());
+        overflow(Long.MIN_VALUE,
+                 FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
+                         .toMillis());
+
+        // Instant + to(TimeUnit) overflow
+        overflow(Long.MAX_VALUE,
+                 FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1))
+                         .to(MILLISECONDS));
+        overflow(Long.MAX_VALUE,
+                 FileTime.from(Instant.ofEpochSecond(Long.MAX_VALUE / 1000,
+                                                     MILLISECONDS.toNanos(1000)))
+                    .to(MILLISECONDS));
+        overflow(Long.MIN_VALUE,
+                 FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1))
+                         .to(MILLISECONDS));
+        overflow(Long.MIN_VALUE,
+                 FileTime.from(Instant.ofEpochSecond(Long.MIN_VALUE / 1000,
+                                                     -MILLISECONDS.toNanos(1)))
+            .to(MILLISECONDS));
+    }
+
+    static void overflow(long minmax, long v) {
+        if (v != minmax)
+            throw new RuntimeException("saturates to Long.MIN/MAX_VALUE expected");
+    }
+
+    static void cmp(long v1, TimeUnit u1, long v2, TimeUnit u2, int expected) {
+        int result = FileTime.from(v1, u1).compareTo(FileTime.from(v2, u2));
+        if (result != expected)
+            throw new RuntimeException("unexpected order");
+    }
+
+    static void cmp(Instant ins, long v2, TimeUnit u2, int expected) {
+        int result = FileTime.from(ins).compareTo(FileTime.from(v2, u2));
+        if (result != expected)
+            throw new RuntimeException("unexpected order");
+    }
+
+    static void eq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
+        FileTime t1 = FileTime.from(v1, u1);
+        FileTime t2 = FileTime.from(v2, u2);
+        if (!t1.equals(t2))
+            throw new RuntimeException("not equal");
+        if (t1.hashCode() != t2.hashCode())
+            throw new RuntimeException("hashCodes should be equal");
+    }
+
+    static void eq(Instant ins, long v2, TimeUnit u2) {
+        FileTime t1 = FileTime.from(ins);
+        FileTime t2 = FileTime.from(v2, u2);
+        if (!t1.equals(t2))
+            throw new RuntimeException("not equal");
+        if (t1.hashCode() != t2.hashCode())
+            throw new RuntimeException("hashCodes should be equal");
+    }
+
+    static void eqTime(long value, TimeUnit unit, Instant instant) {
+        long secs = SECONDS.convert(value, unit);
+        long nanos = NANOSECONDS.convert(value - unit.convert(secs, SECONDS), unit);
+        if (nanos < 0) {    // normalize nanoOfSecond to positive
+            secs -= 1;
+            nanos += 1000_000_000;
+        }
+        if (secs != instant.getEpochSecond() || (int)nanos != instant.getNano()) {
+            System.err.println(" ins=" + instant);
+            throw new RuntimeException("ft and instant are not the same time point");
+        }
+    }
+
+    static void neq(long v1, TimeUnit u1, long v2, TimeUnit u2) {
+        FileTime t1 = FileTime.from(v1, u1);
+        FileTime t2 = FileTime.from(v2, u2);
+        if (t1.equals(t2))
+            throw new RuntimeException("should not be equal");
+    }
+
+    static void neq(Instant ins, long v2, TimeUnit u2) {
+        FileTime t1 = FileTime.from(ins);
+        FileTime t2 = FileTime.from(v2, u2);
+        if (t1.equals(t2))
+            throw new RuntimeException("should not be equal");
+    }
+
+    static void to(long v, TimeUnit unit) {
+        FileTime t = FileTime.from(v, unit);
+        for (TimeUnit u: TimeUnit.values()) {
+            long result = t.to(u);
+            long expected = u.convert(v, unit);
+            if (result != expected) {
+                throw new RuntimeException("unexpected result");
+            }
+        }
+    }
+
+    static void ts(long v, TimeUnit unit, String expected) {
+        String result = FileTime.from(v, unit).toString();
+        if (!result.equals(expected)) {
+            System.err.format("FileTime.from(%d, %s).toString() failed\n", v, unit);
+            System.err.format("Expected: %s\n", expected);
+            System.err.format("     Got: %s\n", result);
+            throw new RuntimeException();
+        }
+    }
+
+    static void ts(Instant instant, String expected) {
+        String result = FileTime.from(instant).toString();
+        if (!result.equals(expected)) {
+            System.err.format("FileTime.from(%s).toString() failed\n", instant);
+            System.err.format("Expected: %s\n", expected);
+            System.err.format("     Got: %s\n", result);
+            throw new RuntimeException();
+        }
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
new file mode 100644
index 0000000..7825837
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/PosixFileAttributeViewTest.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.PosixFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/PosixFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Unit test for PosixFileAttributeView, passing silently if this attribute
+ * view is not available.
+ */
+
+// Android-changed: Renamed from "Basic"
+public class PosixFileAttributeViewTest {
+
+    /**
+     * Use view to update permission to the given mode and check that the
+     * permissions have been updated.
+     */
+    static void testPermissions(Path file, String mode) throws IOException {
+        System.out.format("change mode: %s\n", mode);
+        Set<PosixFilePermission> perms = PosixFilePermissions.fromString(mode);
+
+        // change permissions and re-read them.
+        Files.setPosixFilePermissions(file, perms);
+        Set<PosixFilePermission> current = Files.getPosixFilePermissions(file);
+        if (!current.equals(perms)) {
+            throw new RuntimeException("Actual permissions: " +
+                PosixFilePermissions.toString(current) + ", expected: " +
+                PosixFilePermissions.toString(perms));
+        }
+
+        // repeat test using setAttribute/getAttribute
+        Files.setAttribute(file, "posix:permissions", perms);
+        current = (Set<PosixFilePermission>)Files.getAttribute(file, "posix:permissions");
+        if (!current.equals(perms)) {
+            throw new RuntimeException("Actual permissions: " +
+                PosixFilePermissions.toString(current) + ", expected: " +
+                PosixFilePermissions.toString(perms));
+        }
+    }
+
+    /**
+     * Check that the actual permissions of a file match or make it more
+     * secure than requested
+     */
+    static void checkSecure(Set<PosixFilePermission> requested,
+                            Set<PosixFilePermission> actual)
+    {
+        for (PosixFilePermission perm: actual) {
+            if (!requested.contains(perm)) {
+                throw new RuntimeException("Actual permissions: " +
+                    PosixFilePermissions.toString(actual) + ", requested: " +
+                    PosixFilePermissions.toString(requested) +
+                    " - file is less secure than requested");
+            }
+        }
+    }
+
+    /**
+     * Create file with given mode and check that the file is created with a
+     * mode that is not less secure
+     */
+    static void createWithPermissions(Path file,
+                                      String mode)
+        throws IOException
+    {
+        Set<PosixFilePermission> requested = PosixFilePermissions.fromString(mode);
+        FileAttribute<Set<PosixFilePermission>> attr =
+            PosixFilePermissions.asFileAttribute(requested);
+        System.out.format("create file with mode: %s\n", mode);
+        Files.createFile(file, attr);
+        try {
+            checkSecure(requested,
+                Files.getFileAttributeView(file, PosixFileAttributeView.class)
+                     .readAttributes()
+                     .permissions());
+        } finally {
+            Files.delete(file);
+        }
+
+        System.out.format("create directory with mode: %s\n", mode);
+        Files.createDirectory(file, attr);
+        try {
+            checkSecure(requested,
+                Files.getFileAttributeView(file, PosixFileAttributeView.class)
+                     .readAttributes()
+                     .permissions());
+        } finally {
+            Files.delete(file);
+        }
+    }
+
+    /**
+     * Test the setPermissions/permissions methods.
+     */
+    static void permissionTests(Path dir)
+        throws IOException
+    {
+        System.out.println("-- Permission Tests  --");
+
+        // create file and test updating and reading its permissions
+        Path file = dir.resolve("foo");
+        System.out.format("create %s\n", file);
+        Files.createFile(file);
+        try {
+            // get initial permissions so that we can restore them later
+            PosixFileAttributeView view =
+                Files.getFileAttributeView(file, PosixFileAttributeView.class);
+            Set<PosixFilePermission> save = view.readAttributes()
+                .permissions();
+
+            // test various modes
+            try {
+                testPermissions(file, "---------");
+                testPermissions(file, "r--------");
+                testPermissions(file, "-w-------");
+                testPermissions(file, "--x------");
+                testPermissions(file, "rwx------");
+                testPermissions(file, "---r-----");
+                testPermissions(file, "----w----");
+                testPermissions(file, "-----x---");
+                testPermissions(file, "---rwx---");
+                testPermissions(file, "------r--");
+                testPermissions(file, "-------w-");
+                testPermissions(file, "--------x");
+                testPermissions(file, "------rwx");
+                testPermissions(file, "r--r-----");
+                testPermissions(file, "r--r--r--");
+                testPermissions(file, "rw-rw----");
+                testPermissions(file, "rwxrwx---");
+                testPermissions(file, "rw-rw-r--");
+                testPermissions(file, "r-xr-x---");
+                testPermissions(file, "r-xr-xr-x");
+                testPermissions(file, "rwxrwxrwx");
+            } finally {
+                view.setPermissions(save);
+            }
+        } finally {
+            Files.delete(file);
+        }
+
+        // create link (to file that doesn't exist) and test reading of
+        // permissions
+        if (TestUtil.supportsLinks(dir)) {
+            Path link = dir.resolve("link");
+            System.out.format("create link %s\n", link);
+            Files.createSymbolicLink(link, file);
+            try {
+                PosixFileAttributes attrs =
+                    Files.getFileAttributeView(link,
+                                               PosixFileAttributeView.class,
+                                               NOFOLLOW_LINKS)
+                         .readAttributes();
+                if (!attrs.isSymbolicLink()) {
+                    throw new RuntimeException("not a link");
+                }
+            } finally {
+                Files.delete(link);
+            }
+        }
+
+        System.out.println("OKAY");
+    }
+
+    /**
+     * Test creating a file and directory with initial permissios
+     */
+    static void createTests(Path dir)
+        throws IOException
+    {
+        System.out.println("-- Create Tests  --");
+
+        Path file = dir.resolve("foo");
+
+        createWithPermissions(file, "---------");
+        createWithPermissions(file, "r--------");
+        createWithPermissions(file, "-w-------");
+        createWithPermissions(file, "--x------");
+        createWithPermissions(file, "rwx------");
+        createWithPermissions(file, "---r-----");
+        createWithPermissions(file, "----w----");
+        createWithPermissions(file, "-----x---");
+        createWithPermissions(file, "---rwx---");
+        createWithPermissions(file, "------r--");
+        createWithPermissions(file, "-------w-");
+        createWithPermissions(file, "--------x");
+        createWithPermissions(file, "------rwx");
+        createWithPermissions(file, "r--r-----");
+        createWithPermissions(file, "r--r--r--");
+        createWithPermissions(file, "rw-rw----");
+        createWithPermissions(file, "rwxrwx---");
+        createWithPermissions(file, "rw-rw-r--");
+        createWithPermissions(file, "r-xr-x---");
+        createWithPermissions(file, "r-xr-xr-x");
+        createWithPermissions(file, "rwxrwxrwx");
+
+        System.out.println("OKAY");
+    }
+
+    /**
+     * Test setOwner/setGroup methods - this test simply exercises the
+     * methods to avoid configuration.
+     */
+    static void ownerTests(Path dir)
+        throws IOException
+    {
+        System.out.println("-- Owner Tests  --");
+
+        Path file = dir.resolve("gus");
+        System.out.format("create %s\n", file);
+
+        Files.createFile(file);
+        try {
+
+            // read attributes of directory to get owner/group
+            PosixFileAttributeView view =
+                Files.getFileAttributeView(file, PosixFileAttributeView.class);
+            PosixFileAttributes attrs = view.readAttributes();
+
+            // set to existing owner/group
+            view.setOwner(attrs.owner());
+            view.setGroup(attrs.group());
+
+            // repeat test using set/getAttribute
+            UserPrincipal owner = (UserPrincipal)Files.getAttribute(file, "posix:owner");
+            Files.setAttribute(file, "posix:owner", owner);
+            UserPrincipal group = (UserPrincipal)Files.getAttribute(file, "posix:group");
+            Files.setAttribute(file, "posix:group", group);
+
+        } finally {
+            Files.delete(file);
+        }
+
+        System.out.println("OKAY");
+    }
+
+    /**
+     * Test the lookupPrincipalByName/lookupPrincipalByGroupName methods
+     */
+    static void lookupPrincipalTests(Path dir)
+        throws IOException
+    {
+        System.out.println("-- Lookup UserPrincipal Tests --");
+
+        UserPrincipalLookupService lookupService = dir.getFileSystem()
+            .getUserPrincipalLookupService();
+
+        // read attributes of directory to get owner/group
+        PosixFileAttributes attrs = Files.readAttributes(dir, PosixFileAttributes.class);
+
+        // lookup owner and check it matches file's owner
+        System.out.format("lookup: %s\n", attrs.owner().getName());
+        try {
+            UserPrincipal owner = lookupService.lookupPrincipalByName(attrs.owner().getName());
+            if (owner instanceof GroupPrincipal)
+                throw new RuntimeException("owner is a group?");
+            if (!owner.equals(attrs.owner()))
+                throw new RuntimeException("owner different from file owner");
+        } catch (UserPrincipalNotFoundException x) {
+            System.out.println("user not found - test skipped");
+        }
+
+        // lookup group and check it matches file's group-owner
+        System.out.format("lookup group: %s\n", attrs.group().getName());
+        try {
+            GroupPrincipal group = lookupService.lookupPrincipalByGroupName(attrs.group().getName());
+            if (!group.equals(attrs.group()))
+                throw new RuntimeException("group different from file group-owner");
+        } catch (UserPrincipalNotFoundException x) {
+            System.out.println("group not found - test skipped");
+        }
+
+        // test that UserPrincipalNotFoundException is thrown
+        String invalidPrincipal = "scumbag99";
+        try {
+            System.out.format("lookup: %s\n", invalidPrincipal);
+            lookupService.lookupPrincipalByName(invalidPrincipal);
+            throw new RuntimeException("'" + invalidPrincipal + "' is a valid user?");
+        } catch (UserPrincipalNotFoundException x) {
+        }
+        try {
+            System.out.format("lookup group: %s\n", invalidPrincipal);
+            lookupService.lookupPrincipalByGroupName("idonotexist");
+            throw new RuntimeException("'" + invalidPrincipal + "' is a valid group?");
+        } catch (UserPrincipalNotFoundException x) {
+        }
+        System.out.println("OKAY");
+    }
+
+    /**
+     * Test various exceptions are thrown as expected
+     */
+    @SuppressWarnings("unchecked")
+    static void exceptionsTests(Path dir)
+        throws IOException
+    {
+        System.out.println("-- Exceptions --");
+
+        PosixFileAttributeView view =
+            Files.getFileAttributeView(dir,PosixFileAttributeView.class);
+
+        // NullPointerException
+        try {
+            view.setOwner(null);
+            throw new RuntimeException("NullPointerException not thrown");
+        } catch (NullPointerException x) {
+        }
+        try {
+            view.setGroup(null);
+            throw new RuntimeException("NullPointerException not thrown");
+        } catch (NullPointerException x) {
+        }
+
+        UserPrincipalLookupService lookupService = dir.getFileSystem()
+            .getUserPrincipalLookupService();
+        try {
+            lookupService.lookupPrincipalByName(null);
+            throw new RuntimeException("NullPointerException not thrown");
+        } catch (NullPointerException x) {
+        }
+        try {
+            lookupService.lookupPrincipalByGroupName(null);
+            throw new RuntimeException("NullPointerException not thrown");
+        } catch (NullPointerException x) {
+        }
+        try {
+            view.setPermissions(null);
+            throw new RuntimeException("NullPointerException not thrown");
+        } catch (NullPointerException x) {
+        }
+        try {
+            Set<PosixFilePermission> perms = new HashSet<>();
+            perms.add(null);
+            view.setPermissions(perms);
+            throw new RuntimeException("NullPointerException not thrown");
+        }  catch (NullPointerException x) {
+        }
+
+        // ClassCastException
+        try {
+            Set perms = new HashSet();  // raw type
+            perms.add(new Object());
+            view.setPermissions(perms);
+            throw new RuntimeException("ClassCastException not thrown");
+        }  catch (ClassCastException x) {
+        }
+
+        System.out.println("OKAY");
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() throws IOException {
+        Path dir = TestUtil.createTemporaryDirectory();
+        try {
+            if (!Files.getFileStore(dir).supportsFileAttributeView("posix")) {
+                System.out.println("PosixFileAttributeView not supported");
+                return;
+            }
+
+            permissionTests(dir);
+            createTests(dir);
+            ownerTests(dir);
+            lookupPrincipalTests(dir);
+            exceptionsTests(dir);
+
+        } finally {
+            TestUtil.removeAll(dir);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java b/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
new file mode 100644
index 0000000..6fb3c78
--- /dev/null
+++ b/ojluni/src/test/java/nio/file/attribute/UserDefinedFileAttributeViewTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 4313887 6838333
+ * @summary Unit test for java.nio.file.attribute.UserDefinedFileAttributeView
+ * @library ../..
+ */
+// Android-changed: Adapted from
+// jdk/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java
+// Android-changed: Added package & Test import
+package test.java.nio.file.attribute;
+import org.testng.annotations.Test;
+import test.java.nio.file.TestUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.file.*;
+import static java.nio.file.LinkOption.*;
+import java.nio.file.attribute.*;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Random;
+import java.io.IOException;
+
+// Android-changed: Renamed from "Basic"
+public class UserDefinedFileAttributeViewTest {
+
+    private static Random rand = new Random();
+
+    private static final String ATTR_NAME = "mime_type";
+    private static final String ATTR_VALUE = "text/plain";
+    private static final String ATTR_VALUE2 = "text/html";
+
+    static interface Task {
+        void run() throws Exception;
+    }
+
+    static void tryCatch(Class<? extends Throwable> ex, Task task) {
+        boolean caught = false;
+        try {
+            task.run();
+        } catch (Throwable x) {
+            if (ex.isAssignableFrom(x.getClass())) {
+                caught = true;
+            } else {
+                throw new RuntimeException(x);
+            }
+        }
+        if (!caught)
+            throw new RuntimeException(ex.getName() + " expected");
+    }
+
+    static void expectNullPointerException(Task task) {
+        tryCatch(NullPointerException.class, task);
+    }
+
+    static boolean hasAttribute(UserDefinedFileAttributeView view, String attr)
+        throws IOException
+    {
+        for (String name: view.list()) {
+            if (name.equals(ATTR_NAME))
+                return true;
+        }
+        return false;
+    }
+
+    static void test(Path file, LinkOption... options) throws IOException {
+        final UserDefinedFileAttributeView view =
+            Files.getFileAttributeView(file, UserDefinedFileAttributeView.class, options);
+        ByteBuffer buf = rand.nextBoolean() ?
+            ByteBuffer.allocate(100) : ByteBuffer.allocateDirect(100);
+
+        // Test: write
+        buf.put(ATTR_VALUE.getBytes()).flip();
+        int size = buf.remaining();
+        int nwrote = view.write(ATTR_NAME, buf);
+        if (nwrote != size)
+            throw new RuntimeException("Unexpected number of bytes written");
+
+        // Test: size
+        if (view.size(ATTR_NAME) != size)
+            throw new RuntimeException("Unexpected size");
+
+        // Test: read
+        buf.clear();
+        int nread = view.read(ATTR_NAME, buf);
+        if (nread != size)
+            throw new RuntimeException("Unexpected number of bytes read");
+        buf.flip();
+        String value = Charset.defaultCharset().decode(buf).toString();
+        if (!value.equals(ATTR_VALUE))
+            throw new RuntimeException("Unexpected attribute value");
+
+        // Test: read with insufficient space
+        tryCatch(IOException.class, new Task() {
+            public void run() throws IOException {
+                view.read(ATTR_NAME, ByteBuffer.allocateDirect(1));
+            }});
+
+        // Test: replace value
+        buf.clear();
+        buf.put(ATTR_VALUE2.getBytes()).flip();
+        size = buf.remaining();
+        view.write(ATTR_NAME, buf);
+        if (view.size(ATTR_NAME) != size)
+            throw new RuntimeException("Unexpected size");
+
+        // Test: list
+        if (!hasAttribute(view, ATTR_NAME))
+            throw new RuntimeException("Attribute name not in list");
+
+        // Test: delete
+        view.delete(ATTR_NAME);
+        if (hasAttribute(view, ATTR_NAME))
+            throw new RuntimeException("Attribute name in list");
+
+        // Test: dynamic access
+        String name = "user:" + ATTR_NAME;
+        byte[] valueAsBytes = ATTR_VALUE.getBytes();
+        Files.setAttribute(file, name, valueAsBytes);
+        byte[] actualAsBytes = (byte[])Files.getAttribute(file, name);
+        if (!Arrays.equals(valueAsBytes, actualAsBytes))
+            throw new RuntimeException("Unexpected attribute value");
+        Map<String,?> map = Files.readAttributes(file, name);
+        if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
+            throw new RuntimeException("Unexpected attribute value");
+        map = Files.readAttributes(file, "user:*");
+        if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
+            throw new RuntimeException("Unexpected attribute value");
+    }
+
+    static void miscTests(final Path file) throws IOException {
+        final UserDefinedFileAttributeView view =
+            Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
+        view.write(ATTR_NAME, ByteBuffer.wrap(ATTR_VALUE.getBytes()));
+
+        // NullPointerException
+        final ByteBuffer buf = ByteBuffer.allocate(100);
+
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                view.read(null, buf);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                view.read(ATTR_NAME, null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                view.write(null, buf);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+               view.write(ATTR_NAME, null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                view.size(null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                view.delete(null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                Files.getAttribute(file, null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                Files.getAttribute(file, "user:" + ATTR_NAME, (LinkOption[])null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                Files.setAttribute(file, "user:" + ATTR_NAME, null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                Files.setAttribute(file, null, new byte[0]);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                Files.setAttribute(file, "user: " + ATTR_NAME, new byte[0], (LinkOption[])null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                Files.readAttributes(file, (String)null);
+            }});
+        expectNullPointerException(new Task() {
+            public void run() throws IOException {
+                Files.readAttributes(file, "*", (LinkOption[])null);
+            }});
+
+        // Read-only buffer
+        tryCatch(IllegalArgumentException.class, new Task() {
+            public void run() throws IOException {
+                ByteBuffer buf = ByteBuffer.wrap(ATTR_VALUE.getBytes()).asReadOnlyBuffer();
+                view.write(ATTR_NAME, buf);
+                buf.flip();
+                view.read(ATTR_NAME, buf);
+            }});
+
+        // Zero bytes remaining
+        tryCatch(IOException.class, new Task() {
+            public void run() throws IOException {
+                ByteBuffer buf = buf = ByteBuffer.allocateDirect(100);
+                buf.position(buf.capacity());
+                view.read(ATTR_NAME, buf);
+            }});
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() throws IOException {
+        // create temporary directory to run tests
+        Path dir = TestUtil.createTemporaryDirectory();
+        try {
+            if (!Files.getFileStore(dir).supportsFileAttributeView("user")) {
+                System.out.println("UserDefinedFileAttributeView not supported - skip test");
+                return;
+            }
+
+            // test access to user defined attributes of regular file
+            Path file = dir.resolve("foo.html");
+            Files.createFile(file);
+            try {
+                test(file);
+            } finally {
+                Files.delete(file);
+            }
+
+            // test access to user defined attributes of directory
+            Path subdir = dir.resolve("foo");
+            Files.createDirectory(subdir);
+            try {
+                test(subdir);
+            } finally {
+                Files.delete(subdir);
+            }
+
+            // test access to user defined attributes of sym link
+            if (TestUtil.supportsLinks(dir)) {
+                Path target = dir.resolve("doesnotexist");
+                Path link = dir.resolve("link");
+                Files.createSymbolicLink(link, target);
+                try {
+                    test(link, NOFOLLOW_LINKS);
+                } catch (IOException x) {
+                    // access to attributes of sym link may not be supported
+                } finally {
+                    Files.delete(link);
+                }
+            }
+
+            // misc. tests
+            try {
+                file = dir.resolve("foo.txt");
+                Files.createFile(file);
+                miscTests(dir);
+            } finally {
+                Files.delete(file);
+            }
+
+        } finally {
+            TestUtil.removeAll(dir);
+        }
+    }
+ }
diff --git a/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java b/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
index 1ef3a5b..23ef184 100644
--- a/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
+++ b/ojluni/src/test/java/time/test/java/time/chrono/TestJapaneseChronoImpl.java
@@ -99,7 +99,7 @@
         assertEquals(locale.toString(), "ja_JP_#u-ca-japanese", "Unexpected locale");
 
         // Android changed: Android doesn't return the Japanese Imperial Calendar from getInstance.
-        Calendar cal = java.util.Calendar.getJapanesImperialInstance(TimeZone.getDefault(), locale);
+        Calendar cal = Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), locale);
         assertEquals(cal.getCalendarType(), "japanese", "Unexpected calendar type");
 
         JapaneseDate jDate = JapaneseChronology.INSTANCE.date(isoStartDate);
diff --git a/ojluni/src/test/java/time/test/java/util/TestFormatter.java b/ojluni/src/test/java/time/test/java/util/TestFormatter.java
index afd5fc4..05cb2e7 100644
--- a/ojluni/src/test/java/time/test/java/util/TestFormatter.java
+++ b/ojluni/src/test/java/time/test/java/util/TestFormatter.java
@@ -111,7 +111,7 @@
                 TimeZone tz = TimeZone.getTimeZone(zdt.getZone());
                 Calendar cal;
                 if (calLocale.getLanguage().equals("ja")) {
-                    cal = Calendar.getJapanesImperialInstance(tz, calLocale);
+                    cal = Calendar.getJapaneseImperialInstance(tz, calLocale);
                 } else {
                     cal = Calendar.getInstance(tz, calLocale);
                 }
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index 50c4b84..4285911 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -114,6 +114,7 @@
     ojluni/src/main/java/java/lang/AssertionError.java \
     ojluni/src/main/java/java/lang/AutoCloseable.java \
     ojluni/src/main/java/java/lang/Boolean.java \
+    ojluni/src/main/java/java/lang/BootstrapMethodError.java \
     ojluni/src/main/java/java/lang/Byte.java \
     ojluni/src/main/java/java/lang/Character.java \
     ojluni/src/main/java/java/lang/CharSequence.java \
@@ -240,6 +241,8 @@
     ojluni/src/main/java/java/lang/VirtualMachineError.java \
     ojluni/src/main/java/java/lang/Void.java \
     ojluni/src/main/java/java/lang/invoke/LambdaConversionException.java \
+    ojluni/src/main/java/java/lang/invoke/CallSite.java \
+    ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java \
     ojluni/src/main/java/java/lang/invoke/MethodHandle.java \
     ojluni/src/main/java/java/lang/invoke/MethodHandles.java \
     ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java \
@@ -247,8 +250,10 @@
     ojluni/src/main/java/java/lang/invoke/MethodHandleStatics.java \
     ojluni/src/main/java/java/lang/invoke/MethodType.java \
     ojluni/src/main/java/java/lang/invoke/MethodTypeForm.java \
+    ojluni/src/main/java/java/lang/invoke/MutableCallSite.java \
     ojluni/src/main/java/java/lang/invoke/Stable.java \
     ojluni/src/main/java/java/lang/invoke/Transformers.java \
+    ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java \
     ojluni/src/main/java/java/lang/invoke/WrongMethodTypeException.java \
     ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java \
     ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java \
@@ -1744,10 +1749,10 @@
 # On older platforms : Both sets of stub files are used and core-oj does not contain
 # any of these classes.
 openjdk_lambda_stub_files := \
-    ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
     ojluni/src/lambda/java/java/lang/invoke/LambdaMetafactory.java \
     ojluni/src/lambda/java/java/lang/invoke/SerializedLambda.java
 openjdk_lambda_duplicate_stub_files := \
+    ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
     ojluni/src/lambda/java/java/lang/invoke/MethodHandles.java \
     ojluni/src/lambda/java/java/lang/invoke/LambdaConversionException.java \
     ojluni/src/lambda/java/java/lang/invoke/MethodHandle.java \
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index a1417c3..8bc9937 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -16,6 +16,10 @@
 
 package libcore.java.security;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import java.security.Security;
 import java.security.spec.DSAPrivateKeySpec;
 import java.security.spec.DSAPublicKeySpec;
@@ -36,7 +40,6 @@
 import java.util.Set;
 import javax.crypto.spec.DHPrivateKeySpec;
 import javax.crypto.spec.DHPublicKeySpec;
-import junit.framework.Assert;
 
 /**
  * This class defines expected string names for protocols, key types,
@@ -63,7 +66,7 @@
  * Java &trade; PKCS#11 Reference Guide
  * </a>.
  */
-public final class StandardNames extends Assert {
+public final class StandardNames {
 
     public static final boolean IS_RI
             = !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
@@ -107,7 +110,7 @@
     private static void provide(String type, String algorithm) {
         Set<String> algorithms = PROVIDER_ALGORITHMS.get(type);
         if (algorithms == null) {
-            algorithms = new HashSet();
+            algorithms = new HashSet<String>();
             PROVIDER_ALGORITHMS.put(type, algorithms);
         }
         assertTrue("Duplicate " + type + " " + algorithm,
@@ -490,6 +493,16 @@
             provide("Cipher", "PBEWITHSHAAND40BITRC2-CBC");
             provide("Cipher", "PBEWITHSHAAND40BITRC4");
             provide("Cipher", "PBEWITHSHAANDTWOFISH-CBC");
+            provide("Cipher", "PBEWithHmacSHA1AndAES_128");
+            provide("Cipher", "PBEWithHmacSHA224AndAES_128");
+            provide("Cipher", "PBEWithHmacSHA256AndAES_128");
+            provide("Cipher", "PBEWithHmacSHA384AndAES_128");
+            provide("Cipher", "PBEWithHmacSHA512AndAES_128");
+            provide("Cipher", "PBEWithHmacSHA1AndAES_256");
+            provide("Cipher", "PBEWithHmacSHA224AndAES_256");
+            provide("Cipher", "PBEWithHmacSHA256AndAES_256");
+            provide("Cipher", "PBEWithHmacSHA384AndAES_256");
+            provide("Cipher", "PBEWithHmacSHA512AndAES_256");
             provide("Mac", "PBEWITHHMACSHA");
             provide("Mac", "PBEWITHHMACSHA1");
             provide("SecretKeyFactory", "PBEWITHHMACSHA1");
@@ -529,6 +542,20 @@
             provide("Cipher", "AES/OFB/NOPADDING");
             provide("Cipher", "AES/OFB/PKCS5PADDING");
             provide("Cipher", "AES/OFB/PKCS7PADDING");
+            provide("Cipher", "AES_128/CBC/NOPADDING");
+            provide("Cipher", "AES_128/CBC/PKCS5PADDING");
+            provide("Cipher", "AES_128/CBC/PKCS7PADDING");
+            provide("Cipher", "AES_128/ECB/NOPADDING");
+            provide("Cipher", "AES_128/ECB/PKCS5PADDING");
+            provide("Cipher", "AES_128/ECB/PKCS7PADDING");
+            provide("Cipher", "AES_128/GCM/NOPADDING");
+            provide("Cipher", "AES_256/CBC/NOPADDING");
+            provide("Cipher", "AES_256/CBC/PKCS5PADDING");
+            provide("Cipher", "AES_256/CBC/PKCS7PADDING");
+            provide("Cipher", "AES_256/ECB/NOPADDING");
+            provide("Cipher", "AES_256/ECB/PKCS5PADDING");
+            provide("Cipher", "AES_256/ECB/PKCS7PADDING");
+            provide("Cipher", "AES_256/GCM/NOPADDING");
             provide("Cipher", "DESEDE/CBC/NOPADDING");
             provide("Cipher", "DESEDE/CBC/PKCS5PADDING");
             provide("Cipher", "DESEDE/CBC/PKCS7PADDING");
@@ -576,7 +603,7 @@
             unprovide("AlgorithmParameters", "PBEWithMD5AndDES"); // 1.2.840.113549.1.5.3
 
             // EC support
-            // provide("AlgorithmParameters", "EC");
+            provide("AlgorithmParameters", "EC");
             provide("KeyAgreement", "ECDH");
             provide("KeyFactory", "EC");
             provide("KeyPairGenerator", "EC");
diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java
index ef62a44..3829dc1 100644
--- a/support/src/test/java/libcore/java/security/TestKeyStore.java
+++ b/support/src/test/java/libcore/java/security/TestKeyStore.java
@@ -16,10 +16,14 @@
 
 package libcore.java.security;
 
+import static org.junit.Assert.assertEquals;
+
 import com.android.org.bouncycastle.asn1.DEROctetString;
+import com.android.org.bouncycastle.asn1.x500.X500Name;
 import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
 import com.android.org.bouncycastle.asn1.x509.CRLReason;
 import com.android.org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import com.android.org.bouncycastle.asn1.x509.Extension;
 import com.android.org.bouncycastle.asn1.x509.GeneralName;
 import com.android.org.bouncycastle.asn1.x509.GeneralNames;
 import com.android.org.bouncycastle.asn1.x509.GeneralSubtree;
@@ -27,23 +31,20 @@
 import com.android.org.bouncycastle.asn1.x509.KeyUsage;
 import com.android.org.bouncycastle.asn1.x509.NameConstraints;
 import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.org.bouncycastle.asn1.x509.X509Extensions;
 import com.android.org.bouncycastle.cert.X509CertificateHolder;
+import com.android.org.bouncycastle.cert.X509v3CertificateBuilder;
 import com.android.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import com.android.org.bouncycastle.cert.ocsp.BasicOCSPResp;
 import com.android.org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
 import com.android.org.bouncycastle.cert.ocsp.CertificateID;
 import com.android.org.bouncycastle.cert.ocsp.CertificateStatus;
-import com.android.org.bouncycastle.cert.ocsp.OCSPException;
 import com.android.org.bouncycastle.cert.ocsp.OCSPResp;
 import com.android.org.bouncycastle.cert.ocsp.OCSPRespBuilder;
 import com.android.org.bouncycastle.cert.ocsp.RevokedStatus;
 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.android.org.bouncycastle.operator.DigestCalculatorProvider;
-import com.android.org.bouncycastle.operator.OperatorCreationException;
 import com.android.org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
 import com.android.org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.PrintStream;
@@ -64,7 +65,6 @@
 import java.security.UnrecoverableEntryException;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
@@ -79,7 +79,6 @@
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.security.auth.x500.X500Principal;
-import junit.framework.Assert;
 import libcore.javax.net.ssl.TestKeyManager;
 import libcore.javax.net.ssl.TestTrustManager;
 
@@ -90,7 +89,7 @@
  * Creating a key store is relatively slow, so a singleton instance is
  * accessible via TestKeyStore.get().
  */
-public final class TestKeyStore extends Assert {
+public final class TestKeyStore {
     /** Size of DSA keys to generate for testing. */
     private static final int DSA_KEY_SIZE_BITS = 1024;
 
@@ -156,11 +155,9 @@
         }
     }
 
-    private static final boolean TEST_MANAGERS = true;
     private static final byte[] LOCAL_HOST_ADDRESS = { 127, 0, 0, 1 };
     private static final String LOCAL_HOST_NAME = "localhost";
 
-
     public final KeyStore keyStore;
     public final char[] storePassword;
     public final char[] keyPassword;
@@ -707,43 +704,40 @@
             throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
         }
 
-        X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator();
-        x509cg.setSubjectDN(subject);
-        x509cg.setIssuerDN(issuer);
-        x509cg.setNotBefore(start);
-        x509cg.setNotAfter(end);
-        x509cg.setPublicKey(publicKey);
-        x509cg.setSignatureAlgorithm(signatureAlgorithm);
         if (serialNumber == null) {
             byte[] serialBytes = new byte[16];
             new SecureRandom().nextBytes(serialBytes);
             serialNumber = new BigInteger(1, serialBytes);
         }
-        x509cg.setSerialNumber(serialNumber);
+
+        X509v3CertificateBuilder x509cg = new X509v3CertificateBuilder(
+                X500Name.getInstance(issuer.getEncoded()), serialNumber, start, end,
+                X500Name.getInstance(subject.getEncoded()),
+                SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
         if (keyUsage != 0) {
-            x509cg.addExtension(X509Extensions.KeyUsage,
+            x509cg.addExtension(Extension.keyUsage,
                                 true,
                                 new KeyUsage(keyUsage));
         }
         if (ca) {
-            x509cg.addExtension(X509Extensions.BasicConstraints,
+            x509cg.addExtension(Extension.basicConstraints,
                                 true,
                                 new BasicConstraints(true));
         }
         for (int i = 0; i < extendedKeyUsages.size(); i++) {
             KeyPurposeId keyPurposeId = extendedKeyUsages.get(i);
             boolean critical = criticalExtendedKeyUsages.get(i);
-            x509cg.addExtension(X509Extensions.ExtendedKeyUsage,
+            x509cg.addExtension(Extension.extendedKeyUsage,
                                 critical,
                                 new ExtendedKeyUsage(keyPurposeId));
         }
         for (GeneralName subjectAltName : subjectAltNames) {
-            x509cg.addExtension(X509Extensions.SubjectAlternativeName,
+            x509cg.addExtension(Extension.subjectAlternativeName,
                                 false,
                                 new GeneralNames(subjectAltName).getEncoded());
         }
         if (!permittedNameConstraints.isEmpty() || !excludedNameConstraints.isEmpty()) {
-            x509cg.addExtension(X509Extensions.NameConstraints,
+            x509cg.addExtension(Extension.nameConstraints,
                                 true,
                                 new NameConstraints(permittedNameConstraints.toArray(
                                                         new GeneralSubtree[
@@ -753,7 +747,12 @@
                                                             excludedNameConstraints.size()])));
         }
 
-        X509Certificate x509c = x509cg.generateX509Certificate(privateKey);
+        X509CertificateHolder x509holder = x509cg.build(
+                new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey));
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        X509Certificate x509c = (X509Certificate) certFactory.generateCertificate(
+                new ByteArrayInputStream(x509holder.getEncoded()));
+
         if (StandardNames.IS_RI) {
             /*
              * The RI can't handle the BC EC signature algorithm
diff --git a/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java b/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java
index 4f0b90b..005e931 100644
--- a/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java
+++ b/support/src/test/java/org/apache/harmony/testframework/serialization/SerializationTest.java
@@ -266,7 +266,7 @@
      * @param object - object to be compared
      * @return object's comparator
      */
-    public static SerializableAssert defineComparator(TestCase test, Object object)
+    public static SerializableAssert defineComparator(Object test, Object object)
             throws Exception {
 
         if (test instanceof SerializableAssert) {
@@ -300,7 +300,7 @@
      * @param test - test case
      * @param object - to be compared
      */
-    public static void verifyGolden(TestCase test, Object object) throws Exception {
+    public static void verifyGolden(Object test, Object object) throws Exception {
         verifyGolden(test, object, defineComparator(test, object));
     }
 
@@ -316,7 +316,7 @@
      * @param object - to be compared
      * @param comparator - for comparing (de)serialized objects
      */
-    public static void verifyGolden(TestCase test, Object object, SerializableAssert comparator)
+    public static void verifyGolden(Object test, Object object, SerializableAssert comparator)
             throws Exception {
         assertNotNull("Null comparator", comparator);
         Serializable deserialized = getObject(test, ".golden.ser");
@@ -333,7 +333,7 @@
      * @param test - test case
      * @param objects - array of objects to be compared
      */
-    public static void verifyGolden(TestCase test, Object[] objects) throws Exception {
+    public static void verifyGolden(Object test, Object[] objects) throws Exception {
         assertFalse("Empty array", objects.length == 0);
         verifyGolden(test, objects, defineComparator(test, objects[0]));
     }
@@ -353,7 +353,7 @@
      * @param objects - array of objects to be compared
      * @param comparator - for comparing (de)serialized objects
      */
-    public static void verifyGolden(TestCase test, Object[] objects, SerializableAssert comparator)
+    public static void verifyGolden(Object test, Object[] objects, SerializableAssert comparator)
             throws Exception {
         assertFalse("Empty array", objects.length == 0);
         for (int i = 0; i < objects.length; i++) {
@@ -420,7 +420,7 @@
         }
     }
 
-    private static Serializable getObject(TestCase test, String toAppend) throws Exception {
+    private static Serializable getObject(Object test, String toAppend) throws Exception {
         StringBuilder path = new StringBuilder("/serialization");
         path.append(File.separatorChar);
         path.append(test.getClass().getName().replace('.', File.separatorChar));
diff --git a/tzdata/tools/Android.mk b/tzdata/tools/Android.mk
deleted file mode 100644
index 906d866..0000000
--- a/tzdata/tools/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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)
-
-# 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, src/main)
-LOCAL_JAVACFLAGS := -encoding UTF-8
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tzdata/tools/createIcuUpdateResources.sh b/tzdata/tools/createIcuUpdateResources.sh
deleted file mode 100755
index 2db7132..0000000
--- a/tzdata/tools/createIcuUpdateResources.sh
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/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
deleted file mode 100755
index 05646fc..0000000
--- a/tzdata/tools/createTzDataBundle.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/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
deleted file mode 100644
index cdb004a..0000000
--- a/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 3550c6f..0000000
--- a/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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/testing/prepareTzDataUpdates.sh b/tzdata/tools/testing/prepareTzDataUpdates.sh
deleted file mode 100755
index a9fe10c..0000000
--- a/tzdata/tools/testing/prepareTzDataUpdates.sh
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/bin/bash
-
-# Used as part of manual testing of tzdata update mechanism.
-
-if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
-  echo "Configure your environment with build/envsetup.sh and lunch"
-  exit 1
-fi
-
-# Fail if anything below fails
-set -e
-
-cd $ANDROID_BUILD_TOP
-
-# Get the ICU version by looking at the stubdata file
-ICU_STUB_FILE=$(ls external/icu/icu4c/source/stubdata/icudt*l.dat)
-if [ $(echo "$ICU_STUB_FILE" | wc -l) -ne 1 ]; then
-  echo "Could not find unique ICU version from external/icu/icu4c/source/stubdata/icudt*l.dat"
-  exit 1
-fi
-
-ICU_VERSION=${ICU_STUB_FILE##*icudt}
-ICU_VERSION=${ICU_VERSION%l.dat}
-
-echo "Current ICU version is ${ICU_VERSION}"
-
-# Get the current tzdata version and find both the previous and new versions.
-TZDATA=libc/zoneinfo/tzdata
-
-TZHEADER=$(head -n1 bionic/$TZDATA | cut -c1-11)
-
-TZ_CURRENT=${TZHEADER:6}
-
-let currentYear=${TZ_CURRENT:0:4}
-update=${TZ_CURRENT:4:1}
-
-let currentUpdate=$(LC_CTYPE=C printf '%d' "'$update")
-let previousUpdate=currentUpdate-1
-if [ $previousUpdate -lt 97 ]; then
-  let previousYear=currentYear-1
-  PREVIOUSCOMMIT=$(cd bionic; git log --format=oneline ${TZDATA} | grep -E "${previousYear}.$" | head -n1)
-  TZ_PREVIOUS=$(echo $PREVIOUSCOMMIT | sed "s|.*\(${previousYear}.\)\$|\1|")
-else
-  TZ_PREVIOUS=${currentYear}$(printf "\\$(printf '%03o' "${previousUpdate}")")
-  PREVIOUSCOMMIT=$(cd bionic; git log --format=oneline ${TZDATA} | grep -E "${TZ_PREVIOUS}$" | head -n1)
-fi
-
-let nextUpdate=currentUpdate+1
-TZ_NEXT=${currentYear}$(printf "\\$(printf '%03o' "${nextUpdate}")")
-
-echo "Current version of bionic/${TZDATA} is ${TZ_CURRENT}"
-echo "Previous version of bionic/${TZDATA} is ${TZ_PREVIOUS} from commit:"
-echo "    ${PREVIOUSCOMMIT}"
-echo "Next version of bionic/${TZDATA} is ${TZ_NEXT}"
-
-TMP=$(mktemp -d)
-
-TZ_PREVIOUS_SHA=${PREVIOUSCOMMIT%% *}
-
-TMP_PREVIOUS=${TMP}/${TZ_PREVIOUS}_test
-mkdir -p ${TMP_PREVIOUS}
-
-echo "Copied ${TZ_PREVIOUS} tzdata to ${TMP_PREVIOUS}"
-(cd bionic; git show ${TZ_PREVIOUS_SHA}:${TZDATA} > ${TMP_PREVIOUS}/tzdata)
-
-cd libcore/tzdata/tools
-
-################################################################
-# Preparing previous update version.
-################################################################
-
-# Create an icu_tzdata.dat for the previous tzdata version.
-# Download the archive for the previous tzdata version.
-PREVIOUS_TAR_GZ="tzdata${TZ_PREVIOUS}.tar.gz"
-TMP_PREVIOUS_TAR_GZ=${TMP}/${PREVIOUS_TAR_GZ}
-IANA_PREVIOUS_URL="ftp://ftp.iana.org/tz/releases/${PREVIOUS_TAR_GZ}"
-echo "Downloading archive for ${TZ_PREVIOUS} version from ${IANA_PREVIOUS_URL}"
-wget -O ${TMP_PREVIOUS_TAR_GZ} ftp://ftp.iana.org/tz/releases/tzdata2016d.tar.gz -o ${TMP}/${PREVIOUS_TAR_GZ}.log
-
-ICU_UPDATE_RESOURCES_LOG=${TMP}/icuUpdateResources.log
-echo "Creating icu_tzdata.dat file for ${TZ_PREVIOUS}/ICU ${ICU_VERSION}, may take a while, check ${ICU_UPDATE_RESOURCES_LOG} for details"
-./createIcuUpdateResources.sh ${TMP_PREVIOUS_TAR_GZ} ${ICU_VERSION} &> ${ICU_UPDATE_RESOURCES_LOG}
-mv icu_tzdata.dat ${TMP_PREVIOUS}
-
-TZ_PREVIOUS_UPDATE_PROPERTIES=${TMP}/tzupdate.properties.${TZ_PREVIOUS}
-cat > ${TZ_PREVIOUS_UPDATE_PROPERTIES} <<EOF
-tzdata.version=${TZ_PREVIOUS}
-bionic.file=${TMP_PREVIOUS}/tzdata
-icu.file=${TMP_PREVIOUS}/icu_tzdata.dat
-
-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/icudt${ICU_VERSION}l.dat
-checksum.file.ondevice.2=/system/usr/icu/icudt${ICU_VERSION}l.dat
-EOF
-
-TZ_PREVIOUS_UPDATE_ZIP=update_${TZ_PREVIOUS}_test.zip
-./createTzDataBundle.sh ${TZ_PREVIOUS_UPDATE_PROPERTIES} ${TMP}/update_${TZ_PREVIOUS}_test.zip
-adb push ${TMP}/${TZ_PREVIOUS_UPDATE_ZIP} /data/local/tmp
-echo "Pushed ${TZ_PREVIOUS_UPDATE_ZIP} to /data/local/tmp"
-
-################################################################
-# Preparing next update version.
-################################################################
-
-# Replace the version number in the previous tzdata to create the next version
-TMP_NEXT=${TMP}/${TZ_NEXT}_test
-mkdir -p ${TMP_NEXT}
-sed "1s/^tzdata${TZ_PREVIOUS}/tzdata${TZ_NEXT}/" ${TMP_PREVIOUS}/tzdata > ${TMP_NEXT}/tzdata
-echo "Transformed version ${TZ_PREVIOUS} of tzdata into version ${TZ_NEXT}"
-
-# Replace the version number in the previous icu_tzdata to create the next version
-SEARCH=$(echo ${TZ_PREVIOUS} | sed "s/\(.\)/\1\\\x00/g")
-REPLACE=$(echo ${TZ_NEXT} | sed "s/\(.\)/\1\\\x00/g")
-sed "s/$SEARCH/$REPLACE/" ${TMP_PREVIOUS}/icu_tzdata.dat > ${TMP_NEXT}/icu_tzdata.dat
-echo "Transformed version ${TZ_PREVIOUS} of icu_tzdata into version ${TZ_NEXT}"
-
-TZ_NEXT_UPDATE_PROPERTIES=${TMP}/tzupdate.properties.${TZ_NEXT}
-cat > ${TZ_NEXT_UPDATE_PROPERTIES} <<EOF
-tzdata.version=${TZ_NEXT}
-bionic.file=${TMP_NEXT}/tzdata
-icu.file=${TMP_NEXT}/icu_tzdata.dat
-
-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/icudt${ICU_VERSION}l.dat
-checksum.file.ondevice.2=/system/usr/icu/icudt${ICU_VERSION}l.dat
-EOF
-
-TZ_NEXT_UPDATE_ZIP=update_${TZ_NEXT}_test.zip
-./createTzDataBundle.sh ${TZ_NEXT_UPDATE_PROPERTIES} ${TMP}/update_${TZ_NEXT}_test.zip
-adb push ${TMP}/${TZ_NEXT_UPDATE_ZIP} /data/local/tmp
-echo "Pushed ${TZ_NEXT_UPDATE_ZIP} to /data/local/tmp"
-
-################################################################
-# Preparing bad update version.
-################################################################
-
-TZ_BAD=${TZ_NEXT}_bad_sum
-TZ_BAD_UPDATE_PROPERTIES=${TMP}/tzupdate.properties.${TZ_BAD}
-cat > ${TZ_BAD_UPDATE_PROPERTIES} <<EOF
-tzdata.version=${TZ_BAD}
-bionic.file=${TMP_NEXT}/tzdata
-icu.file=${TMP_NEXT}/icu_tzdata.dat
-
-checksum.file.local.1=${ANDROID_BUILD_TOP}/bionic/${TZDATA}
-checksum.file.ondevice.1=/system/usr/share/zoneinfo/tzdata
-
-# The wrong checksum
-checksum.file.local.2=${ANDROID_BUILD_TOP}/libcore/Android.mk
-checksum.file.ondevice.2=/system/usr/icu/icudt${ICU_VERSION}l.dat
-EOF
-
-TZ_BAD_UPDATE_ZIP=update_${TZ_BAD}_test.zip
-./createTzDataBundle.sh ${TZ_BAD_UPDATE_PROPERTIES} ${TMP}/update_${TZ_BAD}_test.zip
-adb push ${TMP}/${TZ_BAD_UPDATE_ZIP} /data/local/tmp
-echo "Pushed ${TZ_BAD_UPDATE_ZIP} to /data/local/tmp"
-
-################################################################
-# Preparing UpdateTestApp
-################################################################
-
-UPDATE_TEST_APP_LOG=${TMP}/updateTestApp.log
-echo "Building and installing UpdateTestApp, check ${UPDATE_TEST_APP_LOG}"
-cd $ANDROID_BUILD_TOP
-make -j -l8 UpdateTestApp &> ${UPDATE_TEST_APP_LOG}
-adb install -r ${ANDROID_PRODUCT_OUT}/data/app/UpdateTestApp/UpdateTestApp.apk &>> ${UPDATE_TEST_APP_LOG}
-
-echo
-echo "Paste the following into your shell"
-echo "OLD_TZUPDATE=${TMP}/${TZ_PREVIOUS_UPDATE_ZIP}"
-echo "NEW_TZUPDATE=${TMP}/${TZ_NEXT_UPDATE_ZIP}"
-echo "BAD_TZUPDATE=${TMP}/${TZ_BAD_UPDATE_ZIP}"
diff --git a/tzdata/tools/testing/rebootAndGrabLogs.sh b/tzdata/tools/testing/rebootAndGrabLogs.sh
deleted file mode 100755
index a85481f..0000000
--- a/tzdata/tools/testing/rebootAndGrabLogs.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-
-# Used as part of manual testing of tzdata update mechanism.
-
-PREFIX=${1-bootlogs}
-
-echo "Rebooting...."
-adb reboot && adb wait-for-device
-
-TIME=${2-5}
-LOGCAT=${PREFIX}.logcat
-echo "Dumping logcat output in ${LOGCAT}, waiting for ${TIME} seconds"
-adb logcat > ${LOGCAT} 2>/dev/null &
-LOGCAT_PID=$!
-
-sleep ${TIME}
-
-# Kill the logcat process and wait, suppresses the Terminated message
-# output by the shell.
-kill ${LOGCAT_PID}
-wait ${LOGCAT_PID} 2>/dev/null
-
-DMESG=${PREFIX}.dmesg
-echo "Dumping dmesg output in ${DMESG}"
-adb shell dmesg > ${DMESG}
diff --git a/tzdata/tools/tzupdate.properties b/tzdata/tools/tzupdate.properties
deleted file mode 100644
index e3fe002..0000000
--- a/tzdata/tools/tzupdate.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# 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/Android.mk b/tzdata/update/Android.mk
deleted file mode 100644
index 8b45b7f..0000000
--- a/tzdata/update/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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)
-
-# 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, 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, src/test)
-LOCAL_JAVACFLAGS := -encoding UTF-8
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update tzdata_tools junit
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java b/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java
deleted file mode 100644
index b497c85..0000000
--- a/tzdata/update/src/main/libcore/tzdata/update/ConfigBundle.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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();
-                // Note, we assume that nothing will quickly insert a symlink after createSubFile()
-                // that might invalidate the guarantees about name existing beneath targetDir.
-                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
deleted file mode 100644
index 652b786..0000000
--- a/tzdata/update/src/main/libcore/tzdata/update/FileUtils.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * 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 a canonicalized 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 = new File(parentDir, name).getCanonicalFile();
-        if (!subFile.getPath().startsWith(parentDir.getCanonicalPath())) {
-            throw new IOException(name + " must exist beneath " + parentDir +
-                    ". Canonicalized subpath: " + subFile);
-        }
-        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");
-            }
-        }
-    }
-
-    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 {
-        String baseName = file.getName();
-        String canonicalPathExceptBaseName =
-                new File(file.getParentFile().getCanonicalFile(), baseName).getPath();
-        return !file.getCanonicalPath().equals(canonicalPathExceptBaseName);
-    }
-
-    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
deleted file mode 100644
index df0b2a7..0000000
--- a/tzdata/update/src/main/libcore/tzdata/update/TzDataBundleInstaller.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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
deleted file mode 100644
index f1325e7..0000000
--- a/tzdata/update/src/test/libcore/tzdata/update/ConfigBundleTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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
deleted file mode 100644
index d002820..0000000
--- a/tzdata/update/src/test/libcore/tzdata/update/FileUtilsTest.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * 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().getCanonicalFile();
-
-        File actualSubFile = FileUtils.createSubFile(dir1, "file");
-        assertEquals(new File(dir1, "file"), actualSubFile);
-
-        File existingSubFile = createRegularFile(dir1, "file");
-        actualSubFile = FileUtils.createSubFile(dir1, "file");
-        assertEquals(existingSubFile, actualSubFile);
-
-        File existingSubDir = createDir(dir1, "subdir");
-        actualSubFile = FileUtils.createSubFile(dir1, "subdir");
-        assertEquals(existingSubDir, actualSubFile);
-
-        assertCreateSubFileThrows(dir1, "../file");
-        assertCreateSubFileThrows(dir1, "../../file");
-        assertCreateSubFileThrows(dir1, "../otherdir/file");
-
-        File dir2 = createTempDir().getCanonicalFile();
-        createSymlink(dir2, dir1, "symlinkToDir2");
-        assertCreateSubFileThrows(dir1, "symlinkToDir2");
-
-        assertCreateSubFileThrows(dir1, "symlinkToDir2/fileInSymlinkedDir");
-
-        createRegularFile(dir1, "symlinkToDir2/fileInSymlinkedDir");
-        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 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
deleted file mode 100644
index 1825bb3..0000000
--- a/tzdata/update/src/test/libcore/tzdata/update/TzDataBundleInstallerTest.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * 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/update2/src/main/libcore/tzdata/update2/DistroVersion.java b/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
index ae4af45..4848db7 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/DistroVersion.java
@@ -30,12 +30,14 @@
      * The major distro format version supported by this device.
      * Increment this for non-backwards compatible changes to the distro format. Reset the minor
      * version to 1 when doing so.
+     * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
      */
     public static final int CURRENT_FORMAT_MAJOR_VERSION = 1;
 
     /**
      * The minor distro format version supported by this device. Increment this for
      * backwards-compatible changes to the distro format.
+     * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
      */
     public static final int CURRENT_FORMAT_MINOR_VERSION = 1;
 
@@ -122,10 +124,8 @@
     }
 
     public static boolean isCompatibleWithThisDevice(DistroVersion distroVersion) {
-        return (DistroVersion.CURRENT_FORMAT_MAJOR_VERSION
-                == distroVersion.formatMajorVersion)
-                && (DistroVersion.CURRENT_FORMAT_MINOR_VERSION
-                <= distroVersion.formatMinorVersion);
+        return (CURRENT_FORMAT_MAJOR_VERSION == distroVersion.formatMajorVersion)
+                && (CURRENT_FORMAT_MINOR_VERSION <= distroVersion.formatMinorVersion);
     }
 
     @Override
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
index c04b4cb..04e3f2a 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistro.java
@@ -40,6 +40,7 @@
     /**
      * The name of the file inside the distro containing the distro version information.
      * The content is ASCII bytes representing a set of version numbers. See {@link DistroVersion}.
+     * This constant must match the one in system/core/tzdatacheck/tzdatacheck.cpp.
      */
     public static final String DISTRO_VERSION_FILE_NAME = "distro_version";
 
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
similarity index 98%
rename from tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java
rename to tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
index d08bb1d..1b56ebf 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneBundleInstaller.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
@@ -26,8 +26,7 @@
  * A distro-validation / extraction class. Separate from the services code that uses it for easier
  * testing. This class is not thread-safe: callers are expected to handle mutual exclusion.
  */
-// TODO(nfuller) Rename to TimeZoneDistroInstaller
-public final class TimeZoneBundleInstaller {
+public final class TimeZoneDistroInstaller {
     /** {@link #installWithErrorCode(byte[])} result code: Success. */
     public final static int INSTALL_SUCCESS = 0;
     /** {@link #installWithErrorCode(byte[])} result code: Distro corrupt. */
@@ -49,7 +48,7 @@
     private final File currentTzDataDir;
     private final File workingDir;
 
-    public TimeZoneBundleInstaller(String logTag, File systemTzDataFile, File installDir) {
+    public TimeZoneDistroInstaller(String logTag, File systemTzDataFile, File installDir) {
         this.logTag = logTag;
         this.systemTzDataFile = systemTzDataFile;
         oldTzDataDir = new File(installDir, OLD_TZ_DATA_DIR_NAME);
diff --git a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
similarity index 92%
rename from tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java
rename to tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
index 088e7cb..6ae0e56 100644
--- a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneBundleInstallerTest.java
+++ b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
@@ -34,9 +34,9 @@
 import libcore.tzdata.update2.tools.TimeZoneDistroBuilder;
 
 /**
- * Tests for {@link TimeZoneBundleInstaller}.
+ * Tests for {@link TimeZoneDistroInstaller}.
  */
-public class TimeZoneBundleInstallerTest extends TestCase {
+public class TimeZoneDistroInstallerTest extends TestCase {
 
     // OLDER_RULES_VERSION < SYSTEM_RULES_VERSION < NEW_RULES_VERSION < NEWER_RULES_VERSION
     private static final String OLDER_RULES_VERSION = "2030a";
@@ -44,7 +44,7 @@
     private static final String NEW_RULES_VERSION = "2030c";
     private static final String NEWER_RULES_VERSION = "2030d";
 
-    private TimeZoneBundleInstaller installer;
+    private TimeZoneDistroInstaller installer;
     private File tempDir;
     private File testInstallDir;
     private File testSystemTzDataDir;
@@ -61,8 +61,8 @@
         byte[] systemTzDataBytes = createTzData(SYSTEM_RULES_VERSION);
         createFile(testSystemTzDataFile, systemTzDataBytes);
 
-        installer = new TimeZoneBundleInstaller(
-                "TimeZoneBundleInstallerTest", testSystemTzDataFile, testInstallDir);
+        installer = new TimeZoneDistroInstaller(
+                "TimeZoneDistroInstallerTest", testSystemTzDataFile, testInstallDir);
     }
 
     private static File createDirectory(String prefix) throws Exception {
@@ -89,8 +89,8 @@
     /** Tests the an update on a device will fail if the /system tzdata file cannot be found. */
     public void testInstall_badSystemFile() throws Exception {
         File doesNotExist = new File(testSystemTzDataDir, "doesNotExist");
-        TimeZoneBundleInstaller brokenSystemInstaller = new TimeZoneBundleInstaller(
-                "TimeZoneBundleInstallerTest", doesNotExist, testInstallDir);
+        TimeZoneDistroInstaller brokenSystemInstaller = new TimeZoneDistroInstaller(
+                "TimeZoneDistroInstallerTest", doesNotExist, testInstallDir);
         TimeZoneDistro tzData = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
 
         try {
@@ -106,7 +106,7 @@
         TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
 
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(distro.getBytes()));
         assertDistroInstalled(distro);
     }
@@ -117,7 +117,7 @@
     public void testInstall_successfulFirstUpdate_sameVersionAsSystem() throws Exception {
         TimeZoneDistro distro = createValidTimeZoneDistro(SYSTEM_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(distro.getBytes()));
         assertDistroInstalled(distro);
     }
@@ -128,7 +128,7 @@
     public void testInstall_unsuccessfulFirstUpdate_olderVersionThanSystem() throws Exception {
         TimeZoneDistro distro = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_RULES_TOO_OLD,
+                TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
                 installer.installWithErrorCode(distro.getBytes()));
         assertNoContentInstalled();
     }
@@ -139,19 +139,19 @@
     public void testInstall_successfulFollowOnUpdate_newerVersion() throws Exception {
         TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(distro1.getBytes()));
         assertDistroInstalled(distro1);
 
         TimeZoneDistro distro2 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(distro2.getBytes()));
         assertDistroInstalled(distro2);
 
         TimeZoneDistro distro3 = createValidTimeZoneDistro(NEWER_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(distro3.getBytes()));
         assertDistroInstalled(distro3);
     }
@@ -163,13 +163,13 @@
     public void testInstall_unsuccessfulFollowOnUpdate_olderVersion() throws Exception {
         TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(distro1.getBytes()));
         assertDistroInstalled(distro1);
 
         TimeZoneDistro distro2 = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_RULES_TOO_OLD,
+                TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
                 installer.installWithErrorCode(distro2.getBytes()));
         assertDistroInstalled(distro1);
     }
@@ -178,7 +178,7 @@
     public void testInstall_missingTzDataFile() throws Exception {
         TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(installedDistro.getBytes()));
         assertDistroInstalled(installedDistro);
 
@@ -187,7 +187,7 @@
                         .clearTzDataForTests()
                         .buildUnvalidated();
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
                 installer.installWithErrorCode(incompleteDistro.getBytes()));
         assertDistroInstalled(installedDistro);
     }
@@ -196,7 +196,7 @@
     public void testInstall_missingIcuFile() throws Exception {
         TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(installedDistro.getBytes()));
         assertDistroInstalled(installedDistro);
 
@@ -205,7 +205,7 @@
                         .clearIcuDataForTests()
                         .buildUnvalidated();
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
                 installer.installWithErrorCode(incompleteDistro.getBytes()));
         assertDistroInstalled(installedDistro);
     }
@@ -220,7 +220,7 @@
 
         TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_SUCCESS,
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
                 installer.installWithErrorCode(distro.getBytes()));
         assertDistroInstalled(distro);
     }
@@ -234,7 +234,7 @@
                 .clearVersionForTests()
                 .buildUnvalidated();
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
                 installer.installWithErrorCode(distro.getBytes()));
         assertNoContentInstalled();
     }
@@ -248,7 +248,7 @@
                 .replaceFormatVersionForTests(2, 1)
                 .buildUnvalidated();
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
                 installer.installWithErrorCode(distro.getBytes()));
         assertNoContentInstalled();
     }
@@ -265,7 +265,7 @@
 
         TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidFormatVersionBytes);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
                 installer.installWithErrorCode(distro.getBytes()));
         assertNoContentInstalled();
     }
@@ -281,7 +281,7 @@
 
         TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRevisionBytes);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
                 installer.installWithErrorCode(distro.getBytes()));
         assertNoContentInstalled();
     }
@@ -297,7 +297,7 @@
 
         TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRulesVersionBytes);
         assertEquals(
-                TimeZoneBundleInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
+                TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
                 installer.installWithErrorCode(distro.getBytes()));
         assertNoContentInstalled();
     }