Merge "Merge OpenJDK 8 java.net (part 2)"
diff --git a/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java
new file mode 100644
index 0000000..605df4d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java
@@ -0,0 +1,67 @@
+/*
+ * 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 dalvik.annotation.optimization;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An ART runtime built-in optimization for "native" methods to speed up JNI transitions.
+ *
+ * <p>
+ * This has the side-effect of disabling all garbage collections while executing a fast native
+ * method. Use with extreme caution. Any long-running methods must not be marked with
+ * {@code @FastNative} (including usually-fast but generally unbounded methods)!</p>
+ *
+ * <p><b>Deadlock Warning:</b>As a rule of thumb, do not acquire any locks during a fast native
+ * call if they aren't also locally released [before returning to managed code].</p>
+ *
+ * <p>
+ * Say some code does:
+ *
+ * <code>
+ * fast_jni_call_to_grab_a_lock();
+ * does_some_java_work();
+ * fast_jni_call_to_release_a_lock();
+ * </code>
+ *
+ * <p>
+ * This code can lead to deadlocks. Say thread 1 just finishes
+ * {@code fast_jni_call_to_grab_a_lock()} and is in {@code does_some_java_work()}.
+ * GC kicks in and suspends thread 1. Thread 2 now is in {@code fast_jni_call_to_grab_a_lock()}
+ * but is blocked on grabbing the native lock since it's held by thread 1.
+ * Now thread suspension can't finish since thread 2 can't be suspended since it's doing
+ * FastNative JNI.
+ * </p>
+ *
+ * <p>
+ * Normal JNI doesn't have the issue since once it's in native code,
+ * it is considered suspended from java's point of view.
+ * FastNative JNI however doesn't do the state transition done by JNI.
+ * </p>
+ *
+ * <p>
+ * Has no effect when used with non-native methods.
+ * </p>
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.CLASS)  // Save memory, don't instantiate as an object at runtime.
+@Target(ElementType.METHOD)
+public @interface FastNative {}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java
index 8ff7956..e567504 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java
@@ -140,8 +140,8 @@
         }
         try {
             buf.put(buf);
-            fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
-        } catch (ReadOnlyBufferException e) {
+            fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+        } catch (IllegalArgumentException e) {
             // expected
         }
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java
index f2f1ea4..1673c16 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java
@@ -137,8 +137,8 @@
         }
         try {
             buf.put(buf);
-            fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
-        } catch (ReadOnlyBufferException e) {
+            fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+        } catch (IllegalArgumentException e) {
             // expected
         }
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java
index 56a14ba..3aec858 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java
@@ -138,8 +138,8 @@
         }
         try {
             buf.put(buf);
-            fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
-        } catch (ReadOnlyBufferException e) {
+            fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+        } catch (IllegalArgumentException e) {
             // expected
         }
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java
index e618783..f0dcad0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java
@@ -138,8 +138,8 @@
         }
         try {
             buf.put(buf);
-            fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
-        } catch (ReadOnlyBufferException e) {
+            fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+        } catch (IllegalArgumentException e) {
             // expected
         }
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java
index fd6438e..283f4f1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java
@@ -138,8 +138,8 @@
         }
         try {
             buf.put(buf);
-            fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
-        } catch (ReadOnlyBufferException e) {
+            fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+        } catch (IllegalArgumentException e) {
             // expected
         }
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java
index aab913e..8885806 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java
@@ -138,8 +138,8 @@
         }
         try {
             buf.put(buf);
-            fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
-        } catch (ReadOnlyBufferException e) {
+            fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+        } catch (IllegalArgumentException e) {
             // expected
         }
     }
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java
new file mode 100644
index 0000000..12b82b0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.lang.invoke;
+
+import junit.framework.TestCase;
+
+import java.lang.invoke.MethodHandleInfo;
+import java.lang.invoke.MethodType;
+
+import static java.lang.invoke.MethodHandleInfo.*;
+
+public class MethodHandleInfoTest extends TestCase {
+    public void test_toString() {
+        final MethodType type = MethodType.methodType(String.class, String.class);
+        String string = MethodHandleInfo.toString(REF_invokeVirtual, String.class, "concat",  type);
+        assertEquals("invokeVirtual java.lang.String.concat:(String)String", string);
+
+        try {
+            MethodHandleInfo.toString(-1, String.class, "concat", type);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            MethodHandleInfo.toString(REF_invokeVirtual, String.class, null, type);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            MethodHandleInfo.toString(REF_invokeVirtual, null, "concat", type);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            MethodHandleInfo.toString(REF_invokeVirtual, String.class, "concat", null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void test_referenceKindToString() {
+        assertEquals("getField", referenceKindToString(REF_getField));
+        assertEquals("getStatic", referenceKindToString(REF_getStatic));
+        assertEquals("putField", referenceKindToString(REF_putField));
+        assertEquals("putStatic", referenceKindToString(REF_putStatic));
+        assertEquals("invokeVirtual", referenceKindToString(REF_invokeVirtual));
+        assertEquals("invokeStatic", referenceKindToString(REF_invokeStatic));
+        assertEquals("invokeSpecial", referenceKindToString(REF_invokeSpecial));
+        assertEquals("newInvokeSpecial", referenceKindToString(REF_newInvokeSpecial));
+        assertEquals("invokeInterface", referenceKindToString(REF_invokeInterface));
+
+        try {
+            referenceKindToString(-1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            referenceKindToString(256);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java b/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java
deleted file mode 100644
index c9cd3d1..0000000
--- a/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.java.lang.reflect;
-
-import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Repeatable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Proxy;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import junit.framework.TestCase;
-
-public final class AnnotationsTest extends TestCase {
-
-    public void testClassDirectAnnotations() {
-        assertAnnotatedElement(Type.class,
-                AnnotationA.class, AnnotationB.class, RepeatableAnnotation.class);
-        assertAnnotatedElementDeclared(Type.class,
-                AnnotationA.class, AnnotationB.class, RepeatableAnnotation.class);
-    }
-
-    public void testClassInheritedAnnotations() {
-        assertAnnotatedElement(ExtendsType.class, AnnotationB.class);
-        assertAnnotatedElementDeclared(ExtendsType.class);
-    }
-
-    public void testConstructorAnnotations() throws Exception {
-        Constructor<Type> constructor = Type.class.getConstructor();
-        assertAnnotatedElement(constructor, AnnotationA.class, AnnotationC.class);
-    }
-
-    public void testFieldAnnotations() throws Exception {
-        Field field = Type.class.getField("field");
-        assertAnnotatedElement(field, AnnotationA.class, AnnotationD.class);
-    }
-
-    public void testMethodAnnotations() throws Exception {
-        Method method = Type.class.getMethod("method", String.class, String.class);
-        assertAnnotatedElement(method, AnnotationB.class, AnnotationC.class);
-    }
-
-    public void testParameterAnnotations() throws Exception {
-        Method method = Type.class.getMethod("method", String.class, String.class);
-        Annotation[][] noParameterAnnotations = method.getParameterAnnotations();
-        assertEquals(2, noParameterAnnotations.length);
-        assertEquals(set(), annotationsToTypes(noParameterAnnotations[0]));
-        assertEquals(set(), annotationsToTypes(noParameterAnnotations[1]));
-
-        Method parameters = Type.class.getMethod("parameters", String.class, String.class);
-        Annotation[][] parameterAnnotations = parameters.getParameterAnnotations();
-        assertEquals(2, parameterAnnotations.length);
-        assertEquals(set(AnnotationB.class, AnnotationD.class),
-                annotationsToTypes(parameterAnnotations[0]));
-        assertEquals(set(AnnotationC.class, AnnotationD.class),
-                annotationsToTypes(parameterAnnotations[1]));
-    }
-
-    public void testAnnotationDefaults() throws Exception {
-        assertEquals((byte) 5, defaultValue("a"));
-        assertEquals((short) 6, defaultValue("b"));
-        assertEquals(7, defaultValue("c"));
-        assertEquals(8L, defaultValue("d"));
-        assertEquals(9.0f, defaultValue("e"));
-        assertEquals(10.0, defaultValue("f"));
-        assertEquals('k', defaultValue("g"));
-        assertEquals(true, defaultValue("h"));
-        assertEquals(Breakfast.WAFFLES, defaultValue("i"));
-        assertEquals("@" + AnnotationA.class.getName() + "()", defaultValue("j").toString());
-        assertEquals("maple", defaultValue("k"));
-        assertEquals(AnnotationB.class, defaultValue("l"));
-        assertEquals("[1, 2, 3]", Arrays.toString((int[]) defaultValue("m")));
-        assertEquals("[WAFFLES, PANCAKES]", Arrays.toString((Breakfast[]) defaultValue("n")));
-        assertEquals(null, defaultValue("o"));
-        assertEquals(null, defaultValue("p"));
-    }
-
-    private Object defaultValue(String name) throws NoSuchMethodException {
-        return HasDefaultsAnnotation.class.getMethod(name).getDefaultValue();
-    }
-
-    public void testGetEnclosingClass() {
-        assertNull(AnnotationsTest.class.getEnclosingClass());
-        assertEquals(AnnotationsTest.class, Foo.class.getEnclosingClass());
-        assertEquals(AnnotationsTest.class, HasMemberClassesInterface.class.getEnclosingClass());
-        assertEquals(HasMemberClassesInterface.class,
-                HasMemberClassesInterface.D.class.getEnclosingClass());
-        assertEquals(AnnotationsTest.class, Foo.class.getEnclosingClass());
-    }
-
-    public void testGetDeclaringClass() {
-        assertNull(AnnotationsTest.class.getDeclaringClass());
-        assertEquals(AnnotationsTest.class, Foo.class.getDeclaringClass());
-        assertEquals(AnnotationsTest.class, HasMemberClassesInterface.class.getDeclaringClass());
-        assertEquals(HasMemberClassesInterface.class,
-                HasMemberClassesInterface.D.class.getDeclaringClass());
-    }
-
-    public void testGetEnclosingClassIsTransitiveForClassesDefinedInAMethod() {
-        class C {}
-        assertEquals(AnnotationsTest.class, C.class.getEnclosingClass());
-    }
-
-    public void testGetDeclaringClassIsNotTransitiveForClassesDefinedInAMethod() {
-        class C {}
-        assertEquals(null, C.class.getDeclaringClass());
-    }
-
-    public void testGetEnclosingMethodIsNotTransitive() {
-        class C {
-            class D {}
-        }
-        assertEquals(null, C.D.class.getEnclosingMethod());
-    }
-
-    public void testStaticFieldAnonymousClass() {
-        // The class declared in the <clinit> is enclosed by the <clinit>'s class.
-        // http://b/11245138
-        assertEquals(AnnotationsTest.class, staticAnonymous.getClass().getEnclosingClass());
-        // However, because it is anonymous, it has no declaring class.
-        // https://code.google.com/p/android/issues/detail?id=61003
-        assertNull(staticAnonymous.getClass().getDeclaringClass());
-        // Because the class is declared in <clinit> which is not exposed through reflection,
-        // it has no enclosing method or constructor.
-        assertNull(staticAnonymous.getClass().getEnclosingMethod());
-        assertNull(staticAnonymous.getClass().getEnclosingConstructor());
-    }
-
-    public void testGetEnclosingMethodOfTopLevelClass() {
-        assertNull(AnnotationsTest.class.getEnclosingMethod());
-    }
-
-    public void testGetEnclosingConstructorOfTopLevelClass() {
-        assertNull(AnnotationsTest.class.getEnclosingConstructor());
-    }
-
-    public void testClassEnclosedByConstructor() throws Exception {
-        Foo foo = new Foo("string");
-        assertEquals(Foo.class, foo.c.getEnclosingClass());
-        assertEquals(Foo.class.getDeclaredConstructor(String.class),
-                foo.c.getEnclosingConstructor());
-        assertNull(foo.c.getEnclosingMethod());
-        assertNull(foo.c.getDeclaringClass());
-    }
-
-    public void testClassEnclosedByMethod() throws Exception {
-        Foo foo = new Foo();
-        foo.foo("string");
-        assertEquals(Foo.class, foo.c.getEnclosingClass());
-        assertNull(foo.c.getEnclosingConstructor());
-        assertEquals(Foo.class.getDeclaredMethod("foo", String.class),
-                foo.c.getEnclosingMethod());
-        assertNull(foo.c.getDeclaringClass());
-    }
-
-    public void testGetClasses() throws Exception {
-        // getClasses() doesn't include classes inherited from interfaces!
-        assertSetEquals(HasMemberClasses.class.getClasses(),
-                HasMemberClassesSuperclass.B.class, HasMemberClasses.H.class);
-    }
-
-    public void testGetDeclaredClasses() throws Exception {
-        assertSetEquals(HasMemberClasses.class.getDeclaredClasses(),
-                HasMemberClasses.G.class, HasMemberClasses.H.class, HasMemberClasses.I.class,
-                HasMemberClasses.J.class, HasMemberClasses.K.class, HasMemberClasses.L.class);
-    }
-
-    public void testConstructorGetExceptions() throws Exception {
-        assertSetEquals(HasThrows.class.getConstructor().getExceptionTypes(),
-                IOException.class, InvocationTargetException.class, IllegalStateException.class);
-        assertSetEquals(HasThrows.class.getConstructor(Void.class).getExceptionTypes());
-    }
-
-    public void testClassMethodGetExceptions() throws Exception {
-        assertSetEquals(HasThrows.class.getMethod("foo").getExceptionTypes(),
-                IOException.class, InvocationTargetException.class, IllegalStateException.class);
-        assertSetEquals(HasThrows.class.getMethod("foo", Void.class).getExceptionTypes());
-    }
-
-    public void testProxyMethodGetExceptions() throws Exception {
-        InvocationHandler emptyInvocationHandler = new InvocationHandler() {
-            @Override public Object invoke(Object proxy, Method method, Object[] args) {
-                return null;
-            }
-        };
-
-        Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(),
-                new Class[] { ThrowsInterface.class }, emptyInvocationHandler);
-        assertSetEquals(proxy.getClass().getMethod("foo").getExceptionTypes(),
-                IOException.class, InvocationTargetException.class, IllegalStateException.class);
-        assertSetEquals(proxy.getClass().getMethod("foo", Void.class).getExceptionTypes());
-    }
-
-    public void testClassModifiers() {
-        int modifiers = AnnotationsTest.class.getModifiers();
-        assertTrue(Modifier.isPublic(modifiers));
-        assertFalse(Modifier.isProtected(modifiers));
-        assertFalse(Modifier.isPrivate(modifiers));
-        assertFalse(Modifier.isAbstract(modifiers));
-        assertFalse(Modifier.isStatic(modifiers));
-        assertTrue(Modifier.isFinal(modifiers));
-        assertFalse(Modifier.isStrict(modifiers));
-    }
-
-    public void testInnerClassModifiers() {
-        int modifiers = Foo.class.getModifiers();
-        assertFalse(Modifier.isPublic(modifiers));
-        assertFalse(Modifier.isProtected(modifiers));
-        assertTrue(Modifier.isPrivate(modifiers));
-        assertFalse(Modifier.isAbstract(modifiers));
-        assertTrue(Modifier.isStatic(modifiers));
-        assertFalse(Modifier.isFinal(modifiers));
-        assertFalse(Modifier.isStrict(modifiers));
-    }
-
-    public void testAnonymousClassModifiers() {
-        int modifiers = staticAnonymous.getClass().getModifiers();
-        assertFalse(Modifier.isPublic(modifiers));
-        assertFalse(Modifier.isProtected(modifiers));
-        assertFalse(Modifier.isPrivate(modifiers));
-        assertFalse(Modifier.isAbstract(modifiers));
-        assertTrue(Modifier.isStatic(modifiers));
-        assertFalse(Modifier.isFinal(modifiers));
-        assertFalse(Modifier.isStrict(modifiers));
-    }
-
-    public void testInnerClassName() {
-        assertEquals("AnnotationsTest", AnnotationsTest.class.getSimpleName());
-        assertEquals("Foo", Foo.class.getSimpleName());
-        assertEquals("", staticAnonymous.getClass().getSimpleName());
-    }
-
-    public void testIsAnonymousClass() {
-        assertFalse(AnnotationsTest.class.isAnonymousClass());
-        assertFalse(Foo.class.isAnonymousClass());
-        assertTrue(staticAnonymous.getClass().isAnonymousClass());
-    }
-
-    public void testRepeatableAnnotation() {
-        RepeatableAnnotation[] annotations = TypeWithMultipleRepeatableAnnotations.class
-                .getDeclaredAnnotationsByType(RepeatableAnnotation.class);
-        assertNotNull(annotations);
-        assertEquals(2, annotations.length);
-
-        // The non-"WithType" methods will see the wrapper annotation
-        assertAnnotatedElement(TypeWithMultipleRepeatableAnnotations.class,
-                RepeatableAnnotations.class);
-        assertAnnotatedElementDeclared(TypeWithMultipleRepeatableAnnotations.class,
-                RepeatableAnnotations.class);
-    }
-
-    public void testRepeatableAnnotationExplicit() {
-        RepeatableAnnotation[] annotations = TypeWithExplicitRepeatableAnnotations.class
-                .getDeclaredAnnotationsByType(RepeatableAnnotation.class);
-        assertNotNull(annotations);
-        assertEquals(2, annotations.length);
-
-        // The non-"WithType" methods will see the wrapper annotation
-        assertAnnotatedElement(TypeWithExplicitRepeatableAnnotations.class,
-                RepeatableAnnotations.class);
-        assertAnnotatedElementDeclared(TypeWithExplicitRepeatableAnnotations.class,
-                RepeatableAnnotations.class);
-    }
-
-    public void testRepeatableAnnotationOnPackage() {
-        Package aPackage = AnnotationsTest.class.getPackage();
-        RepeatableAnnotation[] annotations = aPackage
-                .getDeclaredAnnotationsByType(RepeatableAnnotation.class);
-        assertNotNull(annotations);
-        assertEquals(2, annotations.length);
-
-        // The non-"WithType" methods will see the wrapper annotation
-        assertPresent(true, aPackage, RepeatableAnnotations.class);
-        assertDeclared(true, aPackage, RepeatableAnnotations.class);
-    }
-
-    public void testRetentionPolicy() {
-        assertNull(RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class));
-        assertNotNull(RetentionAnnotations.class.getAnnotation(RuntimeRetentionAnnotation.class));
-        assertNull(RetentionAnnotations.class.getAnnotation(SourceRetentionAnnotation.class));
-    }
-
-    private static final Object staticAnonymous = new Object() {};
-
-    private static class Foo {
-        Class<?> c;
-        private Foo() {
-        }
-        private Foo(String s) {
-            c = new Object() {}.getClass();
-        }
-        private Foo(int i) {
-            c = new Object() {}.getClass();
-        }
-        private void foo(String s) {
-            c = new Object() {}.getClass();
-        }
-        private void foo(int i) {
-            c = new Object() {}.getClass();
-        }
-    }
-
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface AnnotationA {}
-
-    @Inherited
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface AnnotationB {}
-
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface AnnotationC {}
-
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface AnnotationD {}
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @Repeatable(RepeatableAnnotations.class)
-    public @interface RepeatableAnnotation {}
-
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface RepeatableAnnotations {
-        RepeatableAnnotation[] value();
-    }
-
-    @Retention(RetentionPolicy.CLASS)
-    public @interface ClassRetentionAnnotation {}
-
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface RuntimeRetentionAnnotation {}
-
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SourceRetentionAnnotation {}
-
-    @AnnotationA @AnnotationB @RepeatableAnnotation
-    public static class Type {
-        @AnnotationA @AnnotationC public Type() {}
-        @AnnotationA @AnnotationD public String field;
-        @AnnotationB @AnnotationC public void method(String parameter1, String parameter2) {}
-        @AnnotationB @AnnotationC public void parameters(@AnnotationB @AnnotationD String parameter1,
-                @AnnotationC @AnnotationD String parameter2) {}
-    }
-
-    public static class ExtendsType extends Type {}
-
-    @RepeatableAnnotation
-    @RepeatableAnnotation
-    public static class TypeWithMultipleRepeatableAnnotations {}
-
-    @RepeatableAnnotations({ @RepeatableAnnotation, @RepeatableAnnotation})
-    public static class TypeWithExplicitRepeatableAnnotations {}
-
-    @ClassRetentionAnnotation @RuntimeRetentionAnnotation @SourceRetentionAnnotation
-    public static class RetentionAnnotations {}
-
-    static enum Breakfast { WAFFLES, PANCAKES }
-
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface HasDefaultsAnnotation {
-        byte a() default 5;
-        short b() default 6;
-        int c() default 7;
-        long d() default 8;
-        float e() default 9.0f;
-        double f() default 10.0;
-        char g() default 'k';
-        boolean h() default true;
-        Breakfast i() default Breakfast.WAFFLES;
-        AnnotationA j() default @AnnotationA();
-        String k() default "maple";
-        Class l() default AnnotationB.class;
-        int[] m() default { 1, 2, 3 };
-        Breakfast[] n() default { Breakfast.WAFFLES, Breakfast.PANCAKES };
-        Breakfast o();
-        int p();
-    }
-
-    static class HasMemberClassesSuperclass {
-        class A {}
-        public class B {}
-        static class C {}
-    }
-
-    public interface HasMemberClassesInterface {
-        class D {}
-        public class E {}
-        static class F {}
-    }
-
-    public static class HasMemberClasses extends HasMemberClassesSuperclass
-            implements HasMemberClassesInterface {
-        class G {}
-        public class H {}
-        static class I {}
-        enum J {}
-        interface K {}
-        @interface L {}
-    }
-
-    public static class HasThrows {
-        public HasThrows() throws IOException, InvocationTargetException, IllegalStateException {}
-        public HasThrows(Void v) {}
-        public void foo() throws IOException, InvocationTargetException, IllegalStateException {}
-        public void foo(Void v) {}
-    }
-
-    public static interface ThrowsInterface {
-        void foo() throws IOException, InvocationTargetException, IllegalStateException;
-        void foo(Void v);
-    }
-
-    private void assertAnnotatedElement(
-            AnnotatedElement element, Class<? extends Annotation>... expectedAnnotations) {
-        Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getAnnotations());
-        Set<Class<? extends Annotation>> expectedTypes = set(expectedAnnotations);
-        assertEquals(expectedTypes, actualTypes);
-
-        // getAnnotations() should be consistent with isAnnotationPresent() and getAnnotation()
-        assertPresent(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
-        assertPresent(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
-        assertPresent(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
-        assertPresent(expectedTypes.contains(RepeatableAnnotation.class),
-                element, RepeatableAnnotation.class);
-
-        try {
-            element.isAnnotationPresent(null);
-            fail();
-        } catch (NullPointerException expected) {
-        }
-
-        try {
-            element.getAnnotation(null);
-            fail();
-        } catch (NullPointerException expected) {
-        }
-    }
-
-    private void assertAnnotatedElementDeclared(
-            AnnotatedElement element,
-            Class<? extends Annotation>... expectedDeclaredAnnotations) {
-        Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getDeclaredAnnotations());
-        Set<Class<? extends Annotation>> expectedTypes = set(expectedDeclaredAnnotations);
-        assertEquals(expectedTypes, actualTypes);
-
-        assertDeclared(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
-        assertDeclared(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
-        assertDeclared(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
-        assertDeclared(expectedTypes.contains(RepeatableAnnotation.class),
-                element, RepeatableAnnotation.class);
-
-        try {
-            element.getDeclaredAnnotation(null);
-            fail();
-        } catch (NullPointerException expected) {
-        }
-    }
-
-    private Set<Class<? extends Annotation>> annotationsToTypes(Annotation[] annotations) {
-        Set<Class<? extends Annotation>> result = new HashSet<Class<? extends Annotation>>();
-        for (Annotation annotation : annotations) {
-            result.add(annotation.annotationType());
-        }
-        return result;
-    }
-
-    private void assertPresent(boolean present, AnnotatedElement element,
-            Class<? extends Annotation> annotation) {
-        if (present) {
-            assertNotNull(element.getAnnotation(annotation));
-            assertTrue(element.isAnnotationPresent(annotation));
-        } else {
-            assertNull(element.getAnnotation(annotation));
-            assertFalse(element.isAnnotationPresent(annotation));
-        }
-    }
-
-    private void assertDeclared(boolean present, AnnotatedElement element,
-            Class<? extends Annotation> annotation) {
-        if (present) {
-            assertNotNull(element.getDeclaredAnnotation(annotation));
-        } else {
-            assertNull(element.getDeclaredAnnotation(annotation));
-        }
-    }
-
-    private <T> Set<T> set(T... instances) {
-        return new HashSet<T>(Arrays.asList(instances));
-    }
-
-    private void assertSetEquals(Object[] actual, Object... expected) {
-        Set<Object> actualSet = new HashSet<Object>(Arrays.asList(actual));
-        Set<Object> expectedSet = new HashSet<Object>(Arrays.asList(expected));
-        assertEquals(expectedSet, actualSet);
-    }
-}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
index 1950bf3..f690295 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
@@ -16,22 +16,28 @@
 
 package libcore.java.lang.reflect;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import java.util.AbstractCollection;
 import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.RandomAccess;
 import java.util.Set;
-import junit.framework.Assert;
 import junit.framework.TestCase;
 
 public final class ReflectionTest extends TestCase {
@@ -430,4 +436,215 @@
             void foobar() {}
         }
     }
+
+    public void testGetEnclosingClass() {
+        assertNull(ReflectionTest.class.getEnclosingClass());
+        assertEquals(ReflectionTest.class, Foo.class.getEnclosingClass());
+        assertEquals(ReflectionTest.class, HasMemberClassesInterface.class.getEnclosingClass());
+        assertEquals(HasMemberClassesInterface.class,
+                HasMemberClassesInterface.D.class.getEnclosingClass());
+        assertEquals(ReflectionTest.class, Foo.class.getEnclosingClass());
+    }
+
+    public void testGetDeclaringClass() {
+        assertNull(ReflectionTest.class.getDeclaringClass());
+        assertEquals(ReflectionTest.class, Foo.class.getDeclaringClass());
+        assertEquals(ReflectionTest.class, HasMemberClassesInterface.class.getDeclaringClass());
+        assertEquals(HasMemberClassesInterface.class,
+                HasMemberClassesInterface.D.class.getDeclaringClass());
+    }
+
+    public void testGetEnclosingClassIsTransitiveForClassesDefinedInAMethod() {
+        class C {}
+        assertEquals(ReflectionTest.class, C.class.getEnclosingClass());
+    }
+
+    public void testGetDeclaringClassIsNotTransitiveForClassesDefinedInAMethod() {
+        class C {}
+        assertEquals(null, C.class.getDeclaringClass());
+    }
+
+    public void testGetEnclosingMethodIsNotTransitive() {
+        class C {
+            class D {}
+        }
+        assertEquals(null, C.D.class.getEnclosingMethod());
+    }
+
+    private static final Object staticAnonymous = new Object() {};
+
+    public void testStaticFieldAnonymousClass() {
+        // The class declared in the <clinit> is enclosed by the <clinit>'s class.
+        // http://b/11245138
+        assertEquals(ReflectionTest.class, staticAnonymous.getClass().getEnclosingClass());
+        // However, because it is anonymous, it has no declaring class.
+        // https://code.google.com/p/android/issues/detail?id=61003
+        assertNull(staticAnonymous.getClass().getDeclaringClass());
+        // Because the class is declared in <clinit> which is not exposed through reflection,
+        // it has no enclosing method or constructor.
+        assertNull(staticAnonymous.getClass().getEnclosingMethod());
+        assertNull(staticAnonymous.getClass().getEnclosingConstructor());
+    }
+
+    public void testGetEnclosingMethodOfTopLevelClass() {
+        assertNull(ReflectionTest.class.getEnclosingMethod());
+    }
+
+    public void testGetEnclosingConstructorOfTopLevelClass() {
+        assertNull(ReflectionTest.class.getEnclosingConstructor());
+    }
+
+    public void testClassEnclosedByConstructor() throws Exception {
+        Foo foo = new Foo("string");
+        assertEquals(Foo.class, foo.c.getEnclosingClass());
+        assertEquals(Foo.class.getDeclaredConstructor(String.class),
+                foo.c.getEnclosingConstructor());
+        assertNull(foo.c.getEnclosingMethod());
+        assertNull(foo.c.getDeclaringClass());
+    }
+
+    public void testClassEnclosedByMethod() throws Exception {
+        Foo foo = new Foo();
+        foo.foo("string");
+        assertEquals(Foo.class, foo.c.getEnclosingClass());
+        assertNull(foo.c.getEnclosingConstructor());
+        assertEquals(Foo.class.getDeclaredMethod("foo", String.class),
+                foo.c.getEnclosingMethod());
+        assertNull(foo.c.getDeclaringClass());
+    }
+
+    public void testGetClasses() throws Exception {
+        // getClasses() doesn't include classes inherited from interfaces!
+        assertSetEquals(HasMemberClasses.class.getClasses(),
+                HasMemberClassesSuperclass.B.class, HasMemberClasses.H.class);
+    }
+
+    public void testGetDeclaredClasses() throws Exception {
+        assertSetEquals(HasMemberClasses.class.getDeclaredClasses(),
+                HasMemberClasses.G.class, HasMemberClasses.H.class, HasMemberClasses.I.class,
+                HasMemberClasses.J.class, HasMemberClasses.K.class, HasMemberClasses.L.class);
+    }
+
+    public void testConstructorGetExceptions() throws Exception {
+        assertSetEquals(HasThrows.class.getConstructor().getExceptionTypes(),
+                IOException.class, InvocationTargetException.class, IllegalStateException.class);
+        assertSetEquals(HasThrows.class.getConstructor(Void.class).getExceptionTypes());
+    }
+
+    public void testClassMethodGetExceptions() throws Exception {
+        assertSetEquals(HasThrows.class.getMethod("foo").getExceptionTypes(),
+                IOException.class, InvocationTargetException.class, IllegalStateException.class);
+        assertSetEquals(HasThrows.class.getMethod("foo", Void.class).getExceptionTypes());
+    }
+
+    public void testProxyMethodGetExceptions() throws Exception {
+        InvocationHandler emptyInvocationHandler = new InvocationHandler() {
+            @Override public Object invoke(Object proxy, Method method, Object[] args) {
+                return null;
+            }
+        };
+
+        Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(),
+                new Class[] { ThrowsInterface.class }, emptyInvocationHandler);
+        assertSetEquals(proxy.getClass().getMethod("foo").getExceptionTypes(),
+                IOException.class, InvocationTargetException.class, IllegalStateException.class);
+        assertSetEquals(proxy.getClass().getMethod("foo", Void.class).getExceptionTypes());
+    }
+
+    public void testClassModifiers() {
+        int modifiers = ReflectionTest.class.getModifiers();
+        assertTrue(Modifier.isPublic(modifiers));
+        assertFalse(Modifier.isProtected(modifiers));
+        assertFalse(Modifier.isPrivate(modifiers));
+        assertFalse(Modifier.isAbstract(modifiers));
+        assertFalse(Modifier.isStatic(modifiers));
+        assertTrue(Modifier.isFinal(modifiers));
+        assertFalse(Modifier.isStrict(modifiers));
+    }
+
+    public void testInnerClassModifiers() {
+        int modifiers = Foo.class.getModifiers();
+        assertFalse(Modifier.isPublic(modifiers));
+        assertFalse(Modifier.isProtected(modifiers));
+        assertTrue(Modifier.isPrivate(modifiers));
+        assertFalse(Modifier.isAbstract(modifiers));
+        assertTrue(Modifier.isStatic(modifiers));
+        assertFalse(Modifier.isFinal(modifiers));
+        assertFalse(Modifier.isStrict(modifiers));
+    }
+
+    public void testAnonymousClassModifiers() {
+        int modifiers = staticAnonymous.getClass().getModifiers();
+        assertFalse(Modifier.isPublic(modifiers));
+        assertFalse(Modifier.isProtected(modifiers));
+        assertFalse(Modifier.isPrivate(modifiers));
+        assertFalse(Modifier.isAbstract(modifiers));
+        assertTrue(Modifier.isStatic(modifiers));
+        assertFalse(Modifier.isFinal(modifiers));
+        assertFalse(Modifier.isStrict(modifiers));
+    }
+
+    public void testInnerClassName() {
+        assertEquals("ReflectionTest", ReflectionTest.class.getSimpleName());
+        assertEquals("Foo", Foo.class.getSimpleName());
+        assertEquals("", staticAnonymous.getClass().getSimpleName());
+    }
+
+    private static class Foo {
+        Class<?> c;
+        private Foo() {
+        }
+        private Foo(String s) {
+            c = new Object() {}.getClass();
+        }
+        private Foo(int i) {
+            c = new Object() {}.getClass();
+        }
+        private void foo(String s) {
+            c = new Object() {}.getClass();
+        }
+        private void foo(int i) {
+            c = new Object() {}.getClass();
+        }
+    }
+
+    static class HasMemberClassesSuperclass {
+        class A {}
+        public class B {}
+        static class C {}
+    }
+
+    public interface HasMemberClassesInterface {
+        class D {}
+        public class E {}
+        static class F {}
+    }
+
+    public static class HasMemberClasses extends HasMemberClassesSuperclass
+            implements HasMemberClassesInterface {
+        class G {}
+        public class H {}
+        static class I {}
+        enum J {}
+        interface K {}
+        @interface L {}
+    }
+
+    public static class HasThrows {
+        public HasThrows() throws IOException, InvocationTargetException, IllegalStateException {}
+        public HasThrows(Void v) {}
+        public void foo() throws IOException, InvocationTargetException, IllegalStateException {}
+        public void foo(Void v) {}
+    }
+
+    public static interface ThrowsInterface {
+        void foo() throws IOException, InvocationTargetException, IllegalStateException;
+        void foo(Void v);
+    }
+
+    private void assertSetEquals(Object[] actual, Object... expected) {
+        Set<Object> actualSet = new HashSet<Object>(Arrays.asList(actual));
+        Set<Object> expectedSet = new HashSet<Object>(Arrays.asList(expected));
+        assertEquals(expectedSet, actualSet);
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java
new file mode 100644
index 0000000..ec57469
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java
@@ -0,0 +1,313 @@
+/*
+ * 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.lang.reflect.annotations;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+/**
+ * Utility methods and annotation definitions for use when testing implementations of
+ * AnnotatedElement.
+ *
+ * <p>For compactness, the repeated annotation methods that take strings use a format based on Java
+ * syntax rather than the toString() of annotations. For example, "@Repeated(1)" rather than
+ * "@libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated(value=1)". Use
+ * {@link #EXPECT_EMPTY} to indicate "no annotationed expected".
+ */
+public class AnnotatedElementTestSupport {
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface AnnotationA {}
+
+    @Inherited
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface AnnotationB {}
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface AnnotationC {}
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface AnnotationD {}
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Inherited
+    @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD,
+            ElementType.PARAMETER, ElementType.PACKAGE })
+    public @interface Container {
+        Repeated[] value();
+    }
+
+    @Repeatable(Container.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Inherited
+    @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD,
+            ElementType.PARAMETER, ElementType.PACKAGE })
+    public @interface Repeated {
+        int value();
+    }
+
+    /**
+     * A named constant that can be used with assert methods below that take
+     * "String[] expectedAnnotationStrings" as their final argument to indicate "none".
+     */
+    public static final String[] EXPECT_EMPTY = new String[0];
+
+    private AnnotatedElementTestSupport() {
+    }
+
+    /**
+     * Test the {@link AnnotatedElement} methods associated with "presence". i.e. methods that
+     * deal with annotations being "present" (i.e. "direct" or "inherited" annotations, not
+     * "indirect").
+     *
+     * <p>Asserts that calling {@link AnnotatedElement#getAnnotations()} on the supplied element
+     * returns annotations of the supplied expected classes.
+     *
+     * <p>Where the expected classes contains some subset from
+     * {@link AnnotationA}, {@link AnnotationB} and {@link AnnotationC}, this method also asserts
+     * that {@link AnnotatedElement#isAnnotationPresent(Class)} and
+     * {@link AnnotatedElement#getAnnotation(Class)} works as expected.
+     *
+     * <p>This method also confirms that {@link AnnotatedElement#isAnnotationPresent(Class)} and
+     * {@link AnnotatedElement#getAnnotation(Class)} work correctly with a {@code null} argument.
+     */
+    static void checkAnnotatedElementPresentMethods(
+            AnnotatedElement element, Class<? extends Annotation>... expectedAnnotations) {
+        Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getAnnotations());
+        Set<Class<? extends Annotation>> expectedTypes = set(expectedAnnotations);
+        assertEquals(expectedTypes, actualTypes);
+
+        // getAnnotations() should be consistent with isAnnotationPresent() and getAnnotation()
+        assertPresent(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
+        assertPresent(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
+        assertPresent(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
+
+        try {
+            element.isAnnotationPresent(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            element.getAnnotation(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    /**
+     * Test the {@link AnnotatedElement} methods associated with "direct" annotations.
+     *
+     * <p>Asserts that calling {@link AnnotatedElement#getDeclaredAnnotations()} on the supplied
+     * element returns annotations of the supplied expected classes.
+     *
+     * <p>Where the expected classes contains some subset from
+     * {@link AnnotationA}, {@link AnnotationB} and {@link AnnotationC}, this method also asserts
+     * that {@link AnnotatedElement#getDeclaredAnnotation(Class)} works as expected.
+     *
+     * <p>This method also confirms that {@link AnnotatedElement#isAnnotationPresent(Class)} and
+     * {@link AnnotatedElement#getAnnotation(Class)} work correctly with a {@code null} argument.
+     */
+    static void checkAnnotatedElementDirectMethods(
+            AnnotatedElement element,
+            Class<? extends Annotation>... expectedDeclaredAnnotations) {
+        Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getDeclaredAnnotations());
+        Set<Class<? extends Annotation>> expectedTypes = set(expectedDeclaredAnnotations);
+        assertEquals(expectedTypes, actualTypes);
+
+        assertDeclared(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
+        assertDeclared(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
+        assertDeclared(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
+
+        try {
+            element.getDeclaredAnnotation(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    /**
+     * Extracts the annotation types ({@link Annotation#annotationType()} from the supplied
+     * annotations.
+     */
+    static Set<Class<? extends Annotation>> annotationsToTypes(Annotation[] annotations) {
+        Set<Class<? extends Annotation>> result = new HashSet<Class<? extends Annotation>>();
+        for (Annotation annotation : annotations) {
+            result.add(annotation.annotationType());
+        }
+        return result;
+    }
+
+    private static void assertPresent(boolean present, AnnotatedElement element,
+            Class<? extends Annotation> annotation) {
+        if (present) {
+            assertNotNull(element.getAnnotation(annotation));
+            assertTrue(element.isAnnotationPresent(annotation));
+        } else {
+            assertNull(element.getAnnotation(annotation));
+            assertFalse(element.isAnnotationPresent(annotation));
+        }
+    }
+
+    private static void assertDeclared(boolean present, AnnotatedElement element,
+            Class<? extends Annotation> annotation) {
+        if (present) {
+            assertNotNull(element.getDeclaredAnnotation(annotation));
+        } else {
+            assertNull(element.getDeclaredAnnotation(annotation));
+        }
+    }
+
+    @SafeVarargs
+    static <T> Set<T> set(T... instances) {
+        return new HashSet<>(Arrays.asList(instances));
+    }
+
+    /**
+     * Asserts that {@link AnnotatedElement#isAnnotationPresent(Class)} returns the expected result.
+     */
+    static void assertIsAnnotationPresent(
+            AnnotatedElement element, Class<? extends Annotation> annotationType,
+            boolean expected) {
+        assertEquals("element.isAnnotationPresent() for " + element + " and " + annotationType,
+                expected, element.isAnnotationPresent(annotationType));
+    }
+
+    /**
+     * Asserts that {@link AnnotatedElement#getDeclaredAnnotation(Class)} returns the expected
+     * result. The result is specified using a String. See {@link AnnotatedElementTestSupport} for
+     * the string syntax.
+     */
+    static void assertGetDeclaredAnnotation(AnnotatedElement annotatedElement,
+            Class<? extends Annotation> annotationType, String expectedAnnotationString) {
+        Annotation annotation = annotatedElement.getDeclaredAnnotation(annotationType);
+        assertAnnotationMatches(annotation, expectedAnnotationString);
+    }
+
+    /**
+     * Asserts that {@link AnnotatedElement#getDeclaredAnnotationsByType(Class)} returns the
+     * expected result. The result is specified using a String. See
+     * {@link AnnotatedElementTestSupport} for the string syntax.
+     */
+    static void assertGetDeclaredAnnotationsByType(
+            AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType,
+            String[] expectedAnnotationStrings) {
+        Annotation[] annotations = annotatedElement.getDeclaredAnnotationsByType(annotationType);
+        assertAnnotationsMatch(annotations, expectedAnnotationStrings);
+    }
+
+    /**
+     * Asserts that {@link AnnotatedElement#getAnnotationsByType(Class)} returns the
+     * expected result. The result is specified using a String. See
+     * {@link AnnotatedElementTestSupport} for the string syntax.
+     */
+    static void assertGetAnnotationsByType(AnnotatedElement annotatedElement,
+            Class<? extends Annotation> annotationType, String[] expectedAnnotationStrings)
+            throws Exception {
+        Annotation[] annotations = annotatedElement.getAnnotationsByType(annotationType);
+        assertAnnotationsMatch(annotations, expectedAnnotationStrings);
+    }
+
+    private static void assertAnnotationMatches(
+            Annotation annotation, String expectedAnnotationString) {
+        if (expectedAnnotationString == null) {
+            assertNull(annotation);
+        } else {
+            assertNotNull(annotation);
+            assertEquals(expectedAnnotationString, createAnnotationTestString(annotation));
+        }
+    }
+
+    /**
+     * Asserts that the supplied annotations match the expectation Strings. See
+     * {@link AnnotatedElementTestSupport} for the string syntax.
+     */
+    static void assertAnnotationsMatch(Annotation[] annotations,
+            String[] expectedAnnotationStrings) {
+
+        // Due to Android's dex format insisting that Annotations are sorted by name the ordering of
+        // annotations is determined by the (simple?) name of the Annotation, not just the order
+        // that they are defined in the source. Tests have to be sensitive to that when handling
+        // mixed usage of "Container" and "Repeated" - the "Container" annotations will be
+        // discovered before "Repeated" due to their sort ordering.
+        //
+        // This code assumes that repeated annotations with the same name will be specified in the
+        // source their natural sort order when attributes are considered, just to make the testing
+        // simpler.
+        // e.g. @Repeated(1) @Repeated(2), never @Repeated(2) @Repeated(1)
+
+        // Sorting the expected and actual strings _should_ work providing the assumptions above
+        // hold. It may mask random ordering issues but it's harder to deal with that while the
+        // source ordering is no observed. Providing no developers are ascribing meaning to the
+        // relative order of annotations things should be ok.
+        Arrays.sort(expectedAnnotationStrings);
+
+        String[] actualAnnotationStrings = createAnnotationTestStrings(annotations);
+        Arrays.sort(actualAnnotationStrings);
+
+        assertEquals(
+                Arrays.asList(expectedAnnotationStrings),
+                Arrays.asList(actualAnnotationStrings));
+    }
+
+    private static String[] createAnnotationTestStrings(Annotation[] annotations) {
+        String[] annotationStrings = new String[annotations.length];
+        for (int i = 0; i < annotations.length; i++) {
+            annotationStrings[i] = createAnnotationTestString(annotations[i]);
+        }
+        return annotationStrings;
+    }
+
+    private static String createAnnotationTestString(Annotation annotation) {
+        return "@" + annotation.annotationType().getSimpleName() + createArgumentsTestString(
+                annotation);
+    }
+
+    private static String createArgumentsTestString(Annotation annotation) {
+        if (annotation instanceof Repeated) {
+            Repeated repeated = (Repeated) annotation;
+            return "(" + repeated.value() + ")";
+        } else if (annotation instanceof Container) {
+            Container container = (Container) annotation;
+            String[] repeatedValues = createAnnotationTestStrings(container.value());
+            StringJoiner joiner = new StringJoiner(", ", "{", "}");
+            for (String repeatedValue : repeatedValues) {
+                joiner.add(repeatedValue);
+            }
+            String repeatedValuesString = joiner.toString();
+            return "(" +  repeatedValuesString + ")";
+        }
+        throw new AssertionError("Unknown annotation: " + annotation);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java
similarity index 99%
rename from luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java
rename to luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java
index 60e294b..94c265e 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java
@@ -1,4 +1,4 @@
-package libcore.java.lang.reflect;
+package libcore.java.lang.reflect.annotations;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java
new file mode 100644
index 0000000..a694981
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+
+import dalvik.system.VMRuntime;
+
+/**
+ * Tests for the behavior of Annotation instances at runtime.
+ */
+public class AnnotationsTest extends TestCase {
+
+    enum Breakfast { WAFFLES, PANCAKES }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface HasDefaultsAnnotation {
+        byte a() default 5;
+        short b() default 6;
+        int c() default 7;
+        long d() default 8;
+        float e() default 9.0f;
+        double f() default 10.0;
+        char g() default 'k';
+        boolean h() default true;
+        Breakfast i() default Breakfast.WAFFLES;
+        AnnotationA j() default @AnnotationA();
+        String k() default "maple";
+        Class l() default AnnotationB.class;
+        int[] m() default { 1, 2, 3 };
+        Breakfast[] n() default { Breakfast.WAFFLES, Breakfast.PANCAKES };
+        Breakfast o();
+        int p();
+    }
+
+    public void testAnnotationDefaults() throws Exception {
+        assertEquals((byte) 5, defaultValue("a"));
+        assertEquals((short) 6, defaultValue("b"));
+        assertEquals(7, defaultValue("c"));
+        assertEquals(8L, defaultValue("d"));
+        assertEquals(9.0f, defaultValue("e"));
+        assertEquals(10.0, defaultValue("f"));
+        assertEquals('k', defaultValue("g"));
+        assertEquals(true, defaultValue("h"));
+        assertEquals(Breakfast.WAFFLES, defaultValue("i"));
+        assertEquals("@" + AnnotationA.class.getName() + "()", defaultValue("j").toString());
+        assertEquals("maple", defaultValue("k"));
+        assertEquals(AnnotationB.class, defaultValue("l"));
+        assertEquals("[1, 2, 3]", Arrays.toString((int[]) defaultValue("m")));
+        assertEquals("[WAFFLES, PANCAKES]", Arrays.toString((Breakfast[]) defaultValue("n")));
+        assertEquals(null, defaultValue("o"));
+        assertEquals(null, defaultValue("p"));
+    }
+
+    private static Object defaultValue(String name) throws NoSuchMethodException {
+        return HasDefaultsAnnotation.class.getMethod(name).getDefaultValue();
+    }
+
+    @Retention(RetentionPolicy.CLASS)
+    public @interface ClassRetentionAnnotation {}
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface RuntimeRetentionAnnotation {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SourceRetentionAnnotation {}
+
+    @ClassRetentionAnnotation @RuntimeRetentionAnnotation @SourceRetentionAnnotation
+    public static class RetentionAnnotations {}
+
+    public void testRetentionPolicy() {
+        // b/29500035
+        int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
+        try {
+            // Test N and later behavior
+            VMRuntime.getRuntime().setTargetSdkVersion(24);
+            Annotation classRetentionAnnotation =
+                RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class);
+            assertNull(classRetentionAnnotation);
+
+            // Test pre-N behavior
+            VMRuntime.getRuntime().setTargetSdkVersion(23);
+            classRetentionAnnotation =
+                RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class);
+            assertNotNull(classRetentionAnnotation);
+        } finally {
+            VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
+        }
+        assertNotNull(RetentionAnnotations.class.getAnnotation(RuntimeRetentionAnnotation.class));
+        assertNull(RetentionAnnotations.class.getAnnotation(SourceRetentionAnnotation.class));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java
new file mode 100644
index 0000000..5488ed1
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java
@@ -0,0 +1,266 @@
+/*
+ * 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.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Inherited;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementDirectMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class ClassTest extends TestCase {
+
+    public void setUp() throws Exception {
+        super.setUp();
+        // Required by all the tests.
+        Repeated.class.isAnnotationPresent(Inherited.class);
+    }
+
+    @AnnotationA
+    @AnnotationB
+    private static class Type {
+    }
+
+    public static class ExtendsType extends Type {}
+
+    public void testClassDirectAnnotations() {
+        checkAnnotatedElementPresentMethods(Type.class, AnnotationA.class, AnnotationB.class);
+        checkAnnotatedElementDirectMethods(Type.class, AnnotationA.class, AnnotationB.class);
+    }
+
+    public void testClassInheritedAnnotations() {
+        checkAnnotatedElementPresentMethods(ExtendsType.class, AnnotationB.class);
+        checkAnnotatedElementDirectMethods(ExtendsType.class);
+    }
+
+    @Repeated(1)
+    private static class SingleAnnotation {}
+
+    @Repeated(1)
+    @Repeated(2)
+    private static class MultipleAnnotation {}
+
+    @Container({@Repeated(1)})
+    private static class MultipleAnnotationExplicitSingle {}
+
+    @Repeated(1)
+    @Container({@Repeated(2), @Repeated(3)})
+    private static class MultipleAnnotationOddity {}
+
+    private static class NoAnnotation {}
+
+    private static class InheritedNoNewAnnotation extends SingleAnnotation {}
+
+    @Repeated(2)
+    private static class InheritedSingleWithNewSingleAnnotation extends SingleAnnotation {}
+
+    @Repeated(2)
+    @Repeated(3)
+    private static class InheritedSingleWithNewMultipleAnnotations extends SingleAnnotation {}
+
+    @Repeated(2)
+    private static class InheritedMultipleWithNewSingleAnnotation extends MultipleAnnotation {}
+
+    @Repeated(2)
+    @Repeated(3)
+    private static class InheritedMultipleWithNewMultipleAnnotations extends MultipleAnnotation {}
+
+    public void testIsAnnotationPresent() throws Exception {
+        Class<Repeated> repeated = Repeated.class;
+        assertIsAnnotationPresent(NoAnnotation.class, repeated, false);
+        assertIsAnnotationPresent(SingleAnnotation.class, repeated, true);
+        assertIsAnnotationPresent(MultipleAnnotation.class, repeated, false);
+        assertIsAnnotationPresent(MultipleAnnotationExplicitSingle.class, repeated, false);
+        assertIsAnnotationPresent(MultipleAnnotationOddity.class, repeated, true);
+        assertIsAnnotationPresent(InheritedNoNewAnnotation.class, repeated, true);
+        assertIsAnnotationPresent(InheritedSingleWithNewSingleAnnotation.class, repeated, true);
+        assertIsAnnotationPresent(InheritedSingleWithNewMultipleAnnotations.class, repeated, true);
+        assertIsAnnotationPresent(InheritedMultipleWithNewSingleAnnotation.class, repeated, true);
+        assertIsAnnotationPresent(InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+                false);
+
+        Class<Container> container = Container.class;
+        assertIsAnnotationPresent(NoAnnotation.class, repeated, false);
+        assertIsAnnotationPresent(SingleAnnotation.class, container, false);
+        assertIsAnnotationPresent(MultipleAnnotation.class, container, true);
+        assertIsAnnotationPresent(MultipleAnnotationExplicitSingle.class, container, true);
+        assertIsAnnotationPresent(MultipleAnnotationOddity.class, container, true);
+        assertIsAnnotationPresent(InheritedNoNewAnnotation.class, container, false);
+        assertIsAnnotationPresent(InheritedSingleWithNewSingleAnnotation.class, container, false);
+        assertIsAnnotationPresent(InheritedSingleWithNewMultipleAnnotations.class, container, true);
+        assertIsAnnotationPresent(InheritedMultipleWithNewSingleAnnotation.class, container, true);
+        assertIsAnnotationPresent(InheritedMultipleWithNewMultipleAnnotations.class, container,
+                true);
+    }
+
+    public void testGetDeclaredAnnotation() throws Exception {
+        Class<Repeated> repeated = Repeated.class;
+        assertGetDeclaredAnnotation(NoAnnotation.class, repeated, null);
+        assertGetDeclaredAnnotation(SingleAnnotation.class, repeated, "@Repeated(1)");
+        assertGetDeclaredAnnotation(MultipleAnnotation.class, repeated, null);
+        assertGetDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, repeated, null);
+        assertGetDeclaredAnnotation(MultipleAnnotationOddity.class, repeated, "@Repeated(1)");
+        assertGetDeclaredAnnotation(InheritedNoNewAnnotation.class, repeated, null);
+        assertGetDeclaredAnnotation(InheritedSingleWithNewSingleAnnotation.class, repeated,
+                "@Repeated(2)");
+        assertGetDeclaredAnnotation(InheritedSingleWithNewMultipleAnnotations.class, repeated,
+                null);
+        assertGetDeclaredAnnotation(InheritedMultipleWithNewSingleAnnotation.class, repeated,
+                "@Repeated(2)");
+        assertGetDeclaredAnnotation(InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+                null);
+
+        Class<Container> container = Container.class;
+        assertGetDeclaredAnnotation(NoAnnotation.class, container, null);
+        assertGetDeclaredAnnotation(SingleAnnotation.class, container, null);
+        assertGetDeclaredAnnotation(MultipleAnnotation.class, container,
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, container,
+                "@Container({@Repeated(1)})");
+        assertGetDeclaredAnnotation(MultipleAnnotationOddity.class, container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetDeclaredAnnotation(InheritedNoNewAnnotation.class, container, null);
+        assertGetDeclaredAnnotation(InheritedSingleWithNewSingleAnnotation.class, container, null);
+        assertGetDeclaredAnnotation(InheritedSingleWithNewMultipleAnnotations.class, container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetDeclaredAnnotation(InheritedMultipleWithNewSingleAnnotation.class, container,
+                null);
+        assertGetDeclaredAnnotation(InheritedMultipleWithNewMultipleAnnotations.class, container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+    }
+
+    public void testGetDeclaredAnnotationsByType() throws Exception {
+        Class<Repeated> repeated = Repeated.class;
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                NoAnnotation.class, repeated, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                SingleAnnotation.class, repeated, new String[] { "@Repeated(1)" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                MultipleAnnotation.class, repeated, new String[] { "@Repeated(1)", "@Repeated(2)" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                MultipleAnnotationExplicitSingle.class, repeated, new String[] { "@Repeated(1)" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                MultipleAnnotationOddity.class, repeated,
+                new String[] { "@Repeated(1)", "@Repeated(2)", "@Repeated(3)" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedNoNewAnnotation.class, repeated, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedSingleWithNewSingleAnnotation.class, repeated,
+                new String[] { "@Repeated(2)" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedSingleWithNewMultipleAnnotations.class, repeated,
+                new String[] { "@Repeated(2)", "@Repeated(3)" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedMultipleWithNewSingleAnnotation.class, repeated,
+                new String[] { "@Repeated(2)" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+                new String[] { "@Repeated(2)", "@Repeated(3)" });
+
+        Class<Container> container = Container.class;
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                NoAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                SingleAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                MultipleAnnotation.class, container,
+                new String[] { "@Container({@Repeated(1), @Repeated(2)})" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                MultipleAnnotationExplicitSingle.class, container,
+                new String[] { "@Container({@Repeated(1)})" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                MultipleAnnotationOddity.class, container,
+                new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedNoNewAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedSingleWithNewSingleAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedSingleWithNewMultipleAnnotations.class, container,
+                new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedMultipleWithNewSingleAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                InheritedMultipleWithNewMultipleAnnotations.class, container,
+                new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+    }
+
+    public void testGetAnnotationsByType() throws Exception {
+        Class<Repeated> repeated = Repeated.class;
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                NoAnnotation.class, repeated, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                SingleAnnotation.class, repeated, new String[] { "@Repeated(1)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                MultipleAnnotation.class, repeated, new String[] { "@Repeated(1)", "@Repeated(2)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                MultipleAnnotationExplicitSingle.class, repeated, new String[] { "@Repeated(1)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                MultipleAnnotationOddity.class, repeated,
+                new String[] { "@Repeated(1)", "@Repeated(2)", "@Repeated(3)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedNoNewAnnotation.class, repeated, new String[] { "@Repeated(1)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedSingleWithNewSingleAnnotation.class, repeated,
+                new String[] { "@Repeated(2)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedSingleWithNewMultipleAnnotations.class, repeated,
+                new String[] { "@Repeated(2)", "@Repeated(3)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedMultipleWithNewSingleAnnotation.class, repeated,
+                new String[] { "@Repeated(2)" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+                new String[] { "@Repeated(2)", "@Repeated(3)" });
+
+        Class<Container> container = Container.class;
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                NoAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                SingleAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                MultipleAnnotation.class, container,
+                new String[] { "@Container({@Repeated(1), @Repeated(2)})" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                MultipleAnnotationExplicitSingle.class, container,
+                new String[] { "@Container({@Repeated(1)})" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                MultipleAnnotationOddity.class, container,
+                new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedNoNewAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedSingleWithNewSingleAnnotation.class, container, EXPECT_EMPTY);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedSingleWithNewMultipleAnnotations.class, container,
+                new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedMultipleWithNewSingleAnnotation.class, container,
+                new String[] { "@Container({@Repeated(1), @Repeated(2)})" });
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                InheritedMultipleWithNewMultipleAnnotations.class, container,
+                new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java
new file mode 100644
index 0000000..828b601
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class ConstructorTest extends TestCase {
+
+    private static class Type {
+        @AnnotationA
+        @AnnotationC
+        public Type() {}
+    }
+
+    public void testConstructorAnnotations() throws Exception {
+        Constructor<Type> constructor = Type.class.getConstructor();
+        checkAnnotatedElementPresentMethods(constructor, AnnotationA.class, AnnotationC.class);
+    }
+
+    // A class with multiple constructors that differ by their argument count.
+    private static class AnnotatedClass {
+        @Repeated(1)
+        public AnnotatedClass() {}
+
+        @Repeated(1)
+        @Repeated(2)
+        public AnnotatedClass(int a) {}
+
+        @Container({@Repeated(1)})
+        public AnnotatedClass(int a, int b) {}
+
+        @Repeated(1)
+        @Container({@Repeated(2), @Repeated(3)})
+        public AnnotatedClass(int a, int b, int c) {}
+
+        public AnnotatedClass(int a, int b, int c, int d) {}
+    }
+
+    // Tests for isAnnotationPresent and getDeclaredAnnotation.
+    public void testDeclaredAnnotation() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        checkDeclaredAnnotation(c, 4, repeated, null);
+        checkDeclaredAnnotation(c, 3, repeated, "@Repeated(1)");
+        checkDeclaredAnnotation(c, 2, repeated, null);
+        checkDeclaredAnnotation(c, 1, repeated, null);
+        checkDeclaredAnnotation(c, 0, repeated, "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        checkDeclaredAnnotation(c, 4, container, null);
+        checkDeclaredAnnotation(c, 3, container, "@Container({@Repeated(2), @Repeated(3)})");
+        checkDeclaredAnnotation(c, 2, container, "@Container({@Repeated(1)})");
+        checkDeclaredAnnotation(c, 1, container, "@Container({@Repeated(1), @Repeated(2)})");
+        checkDeclaredAnnotation(c, 0, container, null);
+    }
+
+    private static void checkDeclaredAnnotation(Class<?> c, int constructorArgCount,
+            Class<? extends Annotation> annotationType,
+            String expectedAnnotationString) throws Exception {
+        Constructor constructor = getConstructor(c, constructorArgCount);
+
+        // isAnnotationPresent
+        assertIsAnnotationPresent(constructor, annotationType,
+                expectedAnnotationString != null);
+
+        // getDeclaredAnnotation
+        assertGetDeclaredAnnotation(constructor, annotationType, expectedAnnotationString);
+    }
+
+    public void testGetDeclaredAnnotationsByType() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        assertGetDeclaredAnnotationsByType(c, 4, repeated, EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(c, 3, repeated,
+                "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+        assertGetDeclaredAnnotationsByType(c, 2, repeated, "@Repeated(1)");
+        assertGetDeclaredAnnotationsByType(c, 1, repeated, "@Repeated(1)", "@Repeated(2)");
+        assertGetDeclaredAnnotationsByType(c, 0, repeated, "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        assertGetDeclaredAnnotationsByType(c, 4, container, EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(c, 3, container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetDeclaredAnnotationsByType(c, 2, container, "@Container({@Repeated(1)})");
+        assertGetDeclaredAnnotationsByType(c, 1, container,
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetDeclaredAnnotationsByType(c, 0, container, EXPECT_EMPTY);
+    }
+
+    private static void assertGetDeclaredAnnotationsByType(Class<?> c, int constructorArgCount,
+            Class<? extends Annotation> annotationType,
+            String... expectedAnnotationStrings) throws Exception {
+        Constructor<?> constructor = getConstructor(c, constructorArgCount);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                constructor, annotationType, expectedAnnotationStrings);
+    }
+
+    public void testGetAnnotationsByType() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        assertGetAnnotationsByType(c, 4, repeated, EXPECT_EMPTY);
+        assertGetAnnotationsByType(c, 3, repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+        assertGetAnnotationsByType(c, 2, repeated, "@Repeated(1)");
+        assertGetAnnotationsByType(c, 1, repeated, "@Repeated(1)", "@Repeated(2)");
+        assertGetAnnotationsByType(c, 0, repeated, "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        assertGetAnnotationsByType(c, 4, container, EXPECT_EMPTY);
+        assertGetAnnotationsByType(c, 3, container, "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetAnnotationsByType(c, 2, container, "@Container({@Repeated(1)})");
+        assertGetAnnotationsByType(c, 1, container, "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetAnnotationsByType(c, 0, container, EXPECT_EMPTY);
+    }
+
+    private static void assertGetAnnotationsByType(Class<?> c, int constructorArgCount,
+            Class<? extends Annotation> annotationType,
+            String... expectedAnnotationStrings) throws Exception {
+        Constructor<?> constructor = getConstructor(c, constructorArgCount);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                constructor, annotationType, expectedAnnotationStrings);
+    }
+
+    private static Constructor<?> getConstructor(Class<?> c, int constructorArgCount)
+            throws NoSuchMethodException {
+
+        Class<?>[] args = new Class[constructorArgCount];
+        for (int i = 0; i < constructorArgCount; i++) {
+            args[i] = Integer.TYPE;
+        }
+        return c.getDeclaredConstructor(args);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java
new file mode 100644
index 0000000..8ab7d8e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class FieldTest extends TestCase {
+
+    private static class Type {
+        @AnnotationA
+        @AnnotationD
+        public String field;
+    }
+
+    public void testFieldAnnotations() throws Exception {
+        Field field = Type.class.getField("field");
+        checkAnnotatedElementPresentMethods(field, AnnotationA.class, AnnotationD.class);
+    }
+
+    private static class AnnotatedClass {
+        @Repeated(1)
+        private Object singleAnnotation;
+
+        @Repeated(1)
+        @Repeated(2)
+        private Object multipleAnnotation;
+
+        @Container({@Repeated(1)})
+        private Object multipleAnnotationExplicitSingle;
+
+        @Repeated(1)
+        @Container({@Repeated(2), @Repeated(3)})
+        private Object multipleAnnotationOddity;
+
+        private Object noAnnotation;
+    }
+
+    // Tests for isAnnotationPresent and getDeclaredAnnotation.
+    public void testDeclaredAnnotation() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        checkDeclaredAnnotation(c, "noAnnotation", repeated, null);
+        checkDeclaredAnnotation(c, "multipleAnnotationOddity", repeated, "@Repeated(1)");
+        checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", repeated, null);
+        checkDeclaredAnnotation(c, "multipleAnnotation", repeated, null);
+        checkDeclaredAnnotation(c, "singleAnnotation", repeated, "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        checkDeclaredAnnotation(c, "noAnnotation", container, null);
+        checkDeclaredAnnotation(c, "multipleAnnotationOddity", container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+        checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", container,
+                "@Container({@Repeated(1)})");
+        checkDeclaredAnnotation(c, "multipleAnnotation", container,
+                "@Container({@Repeated(1), @Repeated(2)})");
+        checkDeclaredAnnotation(c, "singleAnnotation", container, null);
+    }
+
+    private static void checkDeclaredAnnotation(
+            Class<?> c, String fieldName, Class<? extends Annotation> annotationType,
+            String expectedAnnotationString) throws Exception {
+        Field field = c.getDeclaredField(fieldName);
+
+        // isAnnotationPresent
+        assertIsAnnotationPresent(field, annotationType, expectedAnnotationString != null);
+
+        // getDeclaredAnnotation
+        assertGetDeclaredAnnotation(field, annotationType, expectedAnnotationString);
+    }
+
+    public void testGetDeclaredAnnotationsByType() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        assertGetDeclaredAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+                "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+        assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+                "@Repeated(1)");
+        assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotation",
+                "@Repeated(1)", "@Repeated(2)");
+        assertGetDeclaredAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        assertGetDeclaredAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationOddity",
+                "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+                "@Container({@Repeated(1)})");
+        assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotation",
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetDeclaredAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+    }
+
+    private static void assertGetDeclaredAnnotationsByType(
+            Class<?> c, Class<? extends Annotation> annotationType, String fieldName,
+            String... expectedAnnotationStrings) throws Exception {
+        Field field = c.getDeclaredField(fieldName);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                field, annotationType, expectedAnnotationStrings);
+    }
+
+    public void testGetAnnotationsByType() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        assertGetAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+        assertGetAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+                "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+        assertGetAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+                "@Repeated(1)");
+        assertGetAnnotationsByType(c, repeated, "multipleAnnotation",
+                "@Repeated(1)", "@Repeated(2)");
+        assertGetAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        assertGetAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+        assertGetAnnotationsByType(c, container, "multipleAnnotationOddity",
+                "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+                "@Container({@Repeated(1)})");
+        assertGetAnnotationsByType(c, container, "multipleAnnotation",
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+    }
+
+    private static void assertGetAnnotationsByType(
+            Class<?> c, Class<? extends Annotation> annotationType,
+            String fieldName, String... expectedAnnotationStrings) throws Exception {
+        Field field = c.getDeclaredField(fieldName);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                field, annotationType, expectedAnnotationStrings);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java
new file mode 100644
index 0000000..c732a9d
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class MethodTest extends TestCase {
+
+    private static class Type {
+        @AnnotationB
+        @AnnotationC
+        public void method(String parameter1, String parameter2) {}
+    }
+
+    public void testMethodAnnotations() throws Exception {
+        Method method = Type.class.getMethod("method", String.class, String.class);
+        checkAnnotatedElementPresentMethods(method, AnnotationB.class, AnnotationC.class);
+    }
+
+    private static class AnnotatedClass {
+        @Repeated(1)
+        public void singleAnnotation() {}
+
+        @Repeated(1)
+        @Repeated(2)
+        public void multipleAnnotation() {}
+
+        @Container({@Repeated(1)})
+        public void multipleAnnotationExplicitSingle() {}
+
+        @Repeated(1)
+        @Container({@Repeated(2), @Repeated(3)})
+        public void multipleAnnotationOddity() {}
+
+        public void noAnnotation() {}
+    }
+
+    // Tests for isAnnotationPresent and getDeclaredAnnotation.
+    public void testDeclaredAnnotation() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        checkDeclaredAnnotation(c, "noAnnotation", repeated, null);
+        checkDeclaredAnnotation(c, "multipleAnnotationOddity", repeated, "@Repeated(1)");
+        checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", repeated, null);
+        checkDeclaredAnnotation(c, "multipleAnnotation", repeated, null);
+        checkDeclaredAnnotation(c, "singleAnnotation", repeated, "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        checkDeclaredAnnotation(c, "noAnnotation", container, null);
+        checkDeclaredAnnotation(c, "multipleAnnotationOddity", container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+        checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", container,
+                "@Container({@Repeated(1)})");
+        checkDeclaredAnnotation(c, "multipleAnnotation", container,
+                "@Container({@Repeated(1), @Repeated(2)})");
+        checkDeclaredAnnotation(c, "singleAnnotation", container, null);
+    }
+
+    private static void checkDeclaredAnnotation(
+            Class<?> c, String methodName, Class<? extends Annotation> annotationType,
+            String expectedAnnotationString) throws Exception {
+        Method method = c.getDeclaredMethod(methodName);
+
+        // isAnnotationPresent
+        assertIsAnnotationPresent(method, annotationType, expectedAnnotationString != null);
+
+        // getDeclaredAnnotation
+        assertGetDeclaredAnnotation(method, annotationType, expectedAnnotationString);
+    }
+
+    public void testGetDeclaredAnnotationsByType() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        assertGetDeclaredAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+                "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+        assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+                "@Repeated(1)");
+        assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotation",
+                "@Repeated(1)", "@Repeated(2)");
+        assertGetDeclaredAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        assertGetDeclaredAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationOddity",
+                "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+                "@Container({@Repeated(1)})");
+        assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotation",
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetDeclaredAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+    }
+
+    private static void assertGetDeclaredAnnotationsByType(
+            Class<?> c, Class<? extends Annotation> annotationType, String methodName,
+            String... expectedAnnotationStrings) throws Exception {
+        Method method = c.getDeclaredMethod(methodName);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                method, annotationType, expectedAnnotationStrings);
+    }
+
+    public void testGetAnnotationsByType() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        Class<? extends Annotation> repeated = Repeated.class;
+        assertGetAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+        assertGetAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+                "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+        assertGetAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+                "@Repeated(1)");
+        assertGetAnnotationsByType(c, repeated, "multipleAnnotation",
+                "@Repeated(1)", "@Repeated(2)");
+        assertGetAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+        Class<? extends Annotation> container = Container.class;
+        assertGetAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+        assertGetAnnotationsByType(c, container, "multipleAnnotationOddity",
+                "@Container({@Repeated(2), @Repeated(3)})");
+        assertGetAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+                "@Container({@Repeated(1)})");
+        assertGetAnnotationsByType(c, container, "multipleAnnotation",
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+    }
+
+    private static void assertGetAnnotationsByType(
+            Class<?> c, Class<? extends Annotation> annotationType,
+            String methodName, String... expectedAnnotationStrings) throws Exception {
+        Method method = c.getDeclaredMethod(methodName);
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                method, annotationType, expectedAnnotationStrings);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java
new file mode 100644
index 0000000..f54a64e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+import libcore.java.lang.reflect.annotations.multipleannotation.MultipleAnnotation;
+import libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle.MultipleAnnotationExplicitSingle;
+import libcore.java.lang.reflect.annotations.multipleannotationoddity.MultipleAnnotationOddity;
+import libcore.java.lang.reflect.annotations.noannotation.NoAnnotation;
+import libcore.java.lang.reflect.annotations.singleannotation.SingleAnnotation;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class PackageTest extends TestCase {
+
+    // Tests for isAnnotationPresent and getDeclaredAnnotation.
+    public void testDeclaredAnnotation() throws Exception {
+        Class<Repeated> repeated = Repeated.class;
+        checkDeclaredAnnotation(NoAnnotation.class, repeated, null);
+        checkDeclaredAnnotation(SingleAnnotation.class, repeated, "@Repeated(1)");
+        checkDeclaredAnnotation(MultipleAnnotation.class, repeated, null);
+        checkDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, repeated, null);
+        checkDeclaredAnnotation(MultipleAnnotationOddity.class, repeated, "@Repeated(1)");
+
+        Class<Container> container = Container.class;
+        checkDeclaredAnnotation(NoAnnotation.class, container, null);
+        checkDeclaredAnnotation(SingleAnnotation.class, container, null);
+        checkDeclaredAnnotation(MultipleAnnotation.class, container,
+                "@Container({@Repeated(1), @Repeated(2)})");
+        checkDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, container,
+                "@Container({@Repeated(1)})");
+        checkDeclaredAnnotation(MultipleAnnotationOddity.class, container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+    }
+
+    private static void checkDeclaredAnnotation(
+            Class<?> classInPackage, Class<? extends Annotation> annotationType,
+            String expectedAnnotationString) throws Exception {
+
+        Package aPackage = classInPackage.getPackage();
+        // isAnnotationPresent
+        assertIsAnnotationPresent(aPackage, annotationType, expectedAnnotationString != null);
+
+        // getDeclaredAnnotation
+        assertGetDeclaredAnnotation(aPackage, annotationType, expectedAnnotationString);
+    }
+
+    public void testGetDeclaredAnnotationsByType() throws Exception {
+        Class<Repeated> repeated = Repeated.class;
+
+        assertGetDeclaredAnnotationsByType(NoAnnotation.class, repeated, EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(SingleAnnotation.class, repeated,
+                "@Repeated(1)");
+        assertGetDeclaredAnnotationsByType(MultipleAnnotation.class, repeated,
+                "@Repeated(1)", "@Repeated(2)");
+        assertGetDeclaredAnnotationsByType(MultipleAnnotationExplicitSingle.class, repeated,
+                "@Repeated(1)");
+        assertGetDeclaredAnnotationsByType(MultipleAnnotationOddity.class, repeated,
+                "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+
+        Class<Container> container = Container.class;
+        assertGetDeclaredAnnotationsByType(NoAnnotation.class, container, EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(SingleAnnotation.class, container, EXPECT_EMPTY);
+        assertGetDeclaredAnnotationsByType(MultipleAnnotation.class, container,
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetDeclaredAnnotationsByType(MultipleAnnotationExplicitSingle.class, container,
+                "@Container({@Repeated(1)})");
+        assertGetDeclaredAnnotationsByType(MultipleAnnotationOddity.class, container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+    }
+
+    private static void assertGetDeclaredAnnotationsByType(
+            Class<?> classInPackage, Class<? extends Annotation> annotationType,
+            String... expectedAnnotationStrings) throws Exception {
+        Package aPackage = classInPackage.getPackage();
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                aPackage, annotationType, expectedAnnotationStrings);
+    }
+
+    public void testGetAnnotationsByType() throws Exception {
+        Class<Repeated> repeated = Repeated.class;
+        assertGetAnnotationsByType(NoAnnotation.class, repeated, EXPECT_EMPTY);
+        assertGetAnnotationsByType(SingleAnnotation.class, repeated, "@Repeated(1)");
+        assertGetAnnotationsByType(MultipleAnnotation.class, repeated,
+                "@Repeated(1)", "@Repeated(2)");
+        assertGetAnnotationsByType(MultipleAnnotationExplicitSingle.class, repeated,
+                "@Repeated(1)");
+        assertGetAnnotationsByType(MultipleAnnotationOddity.class, repeated,
+                "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+
+        Class<Container> container = Container.class;
+        assertGetAnnotationsByType(NoAnnotation.class, container, EXPECT_EMPTY);
+        assertGetAnnotationsByType(SingleAnnotation.class, container, EXPECT_EMPTY);
+        assertGetAnnotationsByType(MultipleAnnotation.class, container,
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertGetAnnotationsByType(MultipleAnnotationExplicitSingle.class, container,
+                "@Container({@Repeated(1)})");
+        assertGetAnnotationsByType(MultipleAnnotationOddity.class, container,
+                "@Container({@Repeated(2), @Repeated(3)})");
+    }
+
+    private static void assertGetAnnotationsByType(Class<?> classInPackage,
+            Class<? extends Annotation> annotationType,
+            String... expectedAnnotationStrings) throws Exception {
+        Package aPackage = classInPackage.getPackage();
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                aPackage, annotationType, expectedAnnotationStrings);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ParameterTest.java
new file mode 100644
index 0000000..f40ac9f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ParameterTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.annotationsToTypes;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertAnnotationsMatch;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.set;
+
+public class ParameterTest extends TestCase {
+    private static class Type {
+        @AnnotationB
+        @AnnotationC
+        public void method(String parameter1, String parameter2) {}
+
+        @AnnotationB
+        @AnnotationC
+        public void parameters(@AnnotationB @AnnotationD String parameter1,
+                @AnnotationC @AnnotationD String parameter2) {}
+    }
+
+    public void testParameterAnnotations() throws Exception {
+        Method method = Type.class.getMethod("method", String.class, String.class);
+        Annotation[][] noParameterAnnotations = method.getParameterAnnotations();
+        assertEquals(2, noParameterAnnotations.length);
+        assertEquals(set(), annotationsToTypes(noParameterAnnotations[0]));
+        assertEquals(set(), annotationsToTypes(noParameterAnnotations[1]));
+
+        Method parameters = Type.class.getMethod("parameters", String.class, String.class);
+        Annotation[][] parameterAnnotations = parameters.getParameterAnnotations();
+        assertEquals(2, parameterAnnotations.length);
+        assertEquals(set(AnnotationB.class, AnnotationD.class),
+                annotationsToTypes(parameterAnnotations[0]));
+        assertEquals(set(AnnotationC.class, AnnotationD.class),
+                annotationsToTypes(parameterAnnotations[1]));
+    }
+
+    private static class AnnotatedClass {
+        public void singleAnnotation(@Repeated(1) String p0) {}
+
+        public void multipleAnnotation(@Repeated(1) @Repeated(2) String p0) {}
+
+        public void multipleAnnotationExplicitSingle(@Container({@Repeated(1)}) String p0) {}
+
+        public void multipleAnnotationOddity(
+                @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) String p0) {}
+
+        public void noAnnotation(String p0) {}
+    }
+
+    public void testGetParameterAnnotations() throws Exception {
+        Class<?> c = AnnotatedClass.class;
+
+        assertParameter0Annotations(c, "noAnnotation", EXPECT_EMPTY);
+        assertParameter0Annotations(c, "multipleAnnotationOddity",
+                "@Repeated(1)", "@Container({@Repeated(2), @Repeated(3)})");
+        assertParameter0Annotations(c, "multipleAnnotationExplicitSingle",
+                "@Container({@Repeated(1)})");
+        assertParameter0Annotations(c, "multipleAnnotation",
+                "@Container({@Repeated(1), @Repeated(2)})");
+        assertParameter0Annotations(c, "singleAnnotation",
+                "@Repeated(1)");
+    }
+
+    private static void assertParameter0Annotations(
+            Class<?> c, String methodName, String... expectedAnnotationStrings) throws Exception {
+        Annotation[][] allAnnotations =
+                c.getDeclaredMethod(methodName, String.class).getParameterAnnotations();
+        final int expectedParameterCount = 1;
+        assertEquals(expectedParameterCount, allAnnotations.length);
+
+        Annotation[] p0Annotations = allAnnotations[0];
+        assertAnnotationsMatch(p0Annotations, expectedAnnotationStrings);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java
similarity index 64%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java
index 1a84c2e..d3e77ae 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java
@@ -13,8 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package libcore.java.lang.reflect.annotations.multipleannotation;
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class MultipleAnnotation {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java
similarity index 71%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java
index 1a84c2e..1ab457e 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Repeated(1)
+@Repeated(2)
+package libcore.java.lang.reflect.annotations.multipleannotation;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java
similarity index 62%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java
index 1a84c2e..d14ff19 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class MultipleAnnotationExplicitSingle {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java
similarity index 64%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java
index 1a84c2e..cf41d87 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Container({@Repeated(1)})
+package libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java
similarity index 63%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java
index 1a84c2e..b85f50f 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.multipleannotationoddity;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class MultipleAnnotationOddity {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java
similarity index 62%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java
index 1a84c2e..afbd99e 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Repeated(1)
+@Container({@Repeated(2), @Repeated(3)})
+package libcore.java.lang.reflect.annotations.multipleannotationoddity;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java
similarity index 65%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java
index 1a84c2e..e7fd957 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.noannotation;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class NoAnnotation {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java
similarity index 81%
rename from luni/src/test/java/libcore/java/lang/reflect/package-info.java
rename to luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java
index 1a84c2e..2942f1a 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+package libcore.java.lang.reflect.annotations.noannotation;
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java
similarity index 64%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java
index 1a84c2e..453b299 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.singleannotation;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class SingleAnnotation {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java
similarity index 72%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java
index 1a84c2e..3686f80 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Repeated(1)
+package libcore.java.lang.reflect.annotations.singleannotation;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
diff --git a/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
index 40c2cac..43cc26d 100644
--- a/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
@@ -24,14 +24,12 @@
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
-import java.net.SocketException;
 import java.net.StandardProtocolFamily;
 import java.net.StandardSocketOptions;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.nio.channels.DatagramChannel;
 import java.nio.channels.MembershipKey;
-import java.util.Enumeration;
 
 public class MembershipKeyTest extends TestCase {
 
@@ -43,7 +41,7 @@
     private final static InetAddress MULTICAST_ADDRESS = getMulticastAddress();
     private final static NetworkInterface NETWORK_INTERFACE = getNetworkInterface();
 
-    private void setup(boolean withSource) throws Exception {
+    private void init(boolean withSource) throws Exception {
         client = DatagramChannel.open(StandardProtocolFamily.INET)
                 .bind(new InetSocketAddress(Inet4Address.ANY, PORT));
         client.configureBlocking(false);
@@ -62,48 +60,48 @@
     }
 
     public void test_isValid_OnChannelCloseWithJoinWithoutSource() throws Exception {
-        setup(false);
-        test_isValid();
+        init(false);
+        check_isValid();
     }
 
     public void test_isValid_OnChannelCloseWithJoinWithSource() throws Exception {
-        setup(true);
-        test_isValid();
+        init(true);
+        check_isValid();
     }
 
-    private void test_isValid() throws IOException {
+    private void check_isValid() throws IOException {
         assertTrue(key.isValid());
         client.close();
         assertFalse(key.isValid());
     }
 
     public void test_isValid_OnDropJoinWithoutSource() throws Exception {
-        setup(false);
-        test_isValid_OnDrop();
+        init(false);
+        check_isValid_OnDrop();
     }
 
     public void test_isValid_OnDropJoinWithSource() throws Exception {
-        setup(true);
-        test_isValid_OnDrop();
+        init(true);
+        check_isValid_OnDrop();
     }
 
-    private void test_isValid_OnDrop() {
+    private void check_isValid_OnDrop() {
         assertTrue(key.isValid());
         key.drop();
         assertFalse(key.isValid());
     }
 
     public void test_dropWithJoinWithoutSource() throws Exception {
-        setup(false);
-        test_drop();
+        init(false);
+        check_drop();
     }
 
     public void test_dropWithJoinWithSource() throws Exception {
-        setup(true);
-        test_drop();
+        init(true);
+        check_drop();
     }
 
-    private void test_drop() throws IOException {
+    private void check_drop() throws IOException {
         key.drop();
         try(DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
             assertEquals(TEST_MESSAGE.length(), dc
@@ -119,55 +117,55 @@
     }
 
     public void test_networkInterface() throws Exception {
-        setup(false);
+        init(false);
         assertEquals(NETWORK_INTERFACE, key.networkInterface());
         client.close();
         assertEquals(NETWORK_INTERFACE, key.networkInterface());
     }
 
     public void test_sourceAddressWithJoinWithSource() throws Exception {
-        setup(true);
+        init(true);
         assertEquals(sourceAddress, key.sourceAddress());
     }
 
     public void test_sourceAddressWithJoinWithoutSource() throws Exception {
-        setup(false);
+        init(false);
         assertNull(key.sourceAddress());
     }
 
     public void test_groupWithJoinWithSource() throws Exception {
-        setup(true);
+        init(true);
         assertEquals(MULTICAST_ADDRESS, key.group());
     }
 
     public void test_groupWithoutJoinWIthSource() throws Exception {
-        setup(false);
+        init(false);
         assertEquals(MULTICAST_ADDRESS, key.group());
     }
 
     public void test_channelWithJoinWithSource() throws Exception {
-        setup(true);
+        init(true);
         assertEquals(client, key.channel());
         key.drop();
         assertEquals(client, key.channel());
     }
 
     public void test_channelWithJoinWithoutSource() throws Exception {
-        setup(false);
+        init(false);
         assertEquals(client, key.channel());
         key.drop();
         assertEquals(client, key.channel());
     }
 
     public void test_blockWithJoinWithSource() throws Exception {
-        setup(true);
+        init(true);
         try {
             key.block(sourceAddress);
         } catch (IllegalStateException expected) {}
     }
 
     public void test_blockWithJoinWithoutSource() throws Exception {
-        setup(false);
+        init(false);
         key.block(sourceAddress);
 
         try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
@@ -184,7 +182,7 @@
     }
 
     public void test_block_Exception () throws Exception {
-        setup(false);
+        init(false);
 
         // Blocking a multicast channel
         try {
@@ -206,7 +204,7 @@
     }
 
     public void test_unblockWithJoinWithSource() throws Exception {
-        setup(true);
+        init(true);
         try {
             key.unblock(Inet4Address.getByName("127.0.0.2"));
             fail();
@@ -214,7 +212,7 @@
     }
 
     public void test_unblockWithJoinWithoutSource() throws Exception {
-        setup(false);
+        init(false);
 
         key.block(sourceAddress);
         key.unblock(sourceAddress);
@@ -238,7 +236,7 @@
     }
 
     public void test_unblock_Exception() throws Exception {
-        setup(false);
+        init(false);
         try {
             key.unblock(sourceAddress);
             fail();
diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java
index e5ba6af..c15aa31 100644
--- a/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java
@@ -35,7 +35,6 @@
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
-import static libcore.java.nio.file.FilesSetup.TEST_DIR;
 import static libcore.java.nio.file.FilesSetup.execCmdAndWaitForTermination;
 import static libcore.java.nio.file.FilesSetup.readFromInputStream;
 import static org.junit.Assert.assertEquals;
@@ -48,7 +47,7 @@
 
     @Test
     public void test_name() throws IOException, InterruptedException {
-        Path path = Paths.get(TEST_DIR, "dir");
+        Path path = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectory(path);
         Process p = execCmdAndWaitForTermination("df", path.toAbsolutePath().toString());
         String shellOutput = readFromInputStream(p.getInputStream()).split("\n")[1];
@@ -58,7 +57,7 @@
 
     @Test
     public void test_type() throws IOException, InterruptedException {
-        Path path = Paths.get(TEST_DIR, "dir");
+        Path path = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectory(path);
         String fileStore = Files.getFileStore(path).name();
         Process p = execCmdAndWaitForTermination("mount");
@@ -104,11 +103,14 @@
 
     @Test
     public void test_supportsFileAttributeView$Class() throws IOException {
-        Path path = Paths.get(TEST_DIR, "dir");
+        Path path = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectories(path);
-        assertTrue(Files.getFileStore(path).supportsFileAttributeView(BasicFileAttributeView.class));
-        assertTrue(Files.getFileStore(path).supportsFileAttributeView(FileOwnerAttributeView.class));
-        assertTrue(Files.getFileStore(path).supportsFileAttributeView(PosixFileAttributeView.class));
+        assertTrue(Files.getFileStore(path).supportsFileAttributeView(
+                BasicFileAttributeView.class));
+        assertTrue(Files.getFileStore(path).supportsFileAttributeView(
+                FileOwnerAttributeView.class));
+        assertTrue(Files.getFileStore(path).supportsFileAttributeView(
+                PosixFileAttributeView.class));
         assertFalse(Files.getFileStore(path).supportsFileAttributeView(DosFileAttributeView.class));
         assertFalse(Files.getFileStore(path).
                 supportsFileAttributeView(UserDefinedFileAttributeView.class));
@@ -118,7 +120,7 @@
 
     @Test
     public void test_supportsFileAttributeView$String() throws IOException {
-        Path path = Paths.get(TEST_DIR, "dir");
+        Path path = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectories(path);
         assertTrue(Files.getFileStore(path).supportsFileAttributeView("basic"));
         assertTrue(Files.getFileStore(path).supportsFileAttributeView("unix"));
@@ -131,9 +133,10 @@
 
     @Test
     public void test_getFileStoreAttributeView() throws IOException {
-        Path path = Paths.get(TEST_DIR, "dir");
+        Path path = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectories(path);
-        assertNull(Files.getFileStore(path).getFileStoreAttributeView(FileStoreAttributeView.class));
+        assertNull(Files.getFileStore(path).getFileStoreAttributeView(
+                FileStoreAttributeView.class));
         try {
             Files.getFileStore(path).getFileStoreAttributeView(null);
             fail();
@@ -142,7 +145,7 @@
 
     @Test
     public void test_getAttribute() throws IOException {
-        Path p = Paths.get(TEST_DIR, "dir");
+        Path p = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectories(p);
         FileStore store = Files.getFileStore(p);
         assertEquals(store.getTotalSpace(), store.getAttribute("totalSpace"));
diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java
index 97f73fd..3b1d65a 100644
--- a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java
@@ -19,8 +19,6 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -65,22 +63,17 @@
 import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
 import static java.nio.file.StandardOpenOption.WRITE;
 import static libcore.java.nio.file.FilesSetup.DATA_FILE;
-import static libcore.java.nio.file.FilesSetup.DATA_FILE_PATH;
 import static libcore.java.nio.file.FilesSetup.NonStandardOption;
-import static libcore.java.nio.file.FilesSetup.TEST_DIR;
 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.TEST_PATH;
 import static libcore.java.nio.file.FilesSetup.readFromFile;
 import static libcore.java.nio.file.FilesSetup.readFromInputStream;
-import static libcore.java.nio.file.FilesSetup.reset;
 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.assertTrue;
 import static org.junit.Assert.fail;
 
-@RunWith(JUnit4.class)
 public class DefaultFileSystemProviderTest {
 
     @Rule
@@ -90,12 +83,12 @@
 
     @Before
     public void setUp() throws Exception {
-        provider = DATA_FILE_PATH.getFileSystem().provider();
+        provider = filesSetup.getDataFilePath().getFileSystem().provider();
     }
 
     @Test
     public void test_newInputStream() throws IOException {
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH, READ)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), READ)) {
             assertEquals(TEST_FILE_DATA, readFromInputStream(is));
         }
     }
@@ -103,33 +96,34 @@
     @Test
     public void test_newInputStream_openOption() throws IOException {
         // Write and Append are not supported.
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH, WRITE)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), WRITE)) {
             fail();
         } catch (UnsupportedOperationException expected) {
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH, APPEND)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), APPEND)) {
             fail();
         } catch (UnsupportedOperationException expected) {
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH, NonStandardOption.OPTION1)){
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(),
+                NonStandardOption.OPTION1)){
             fail();
         } catch (UnsupportedOperationException expected) {
         }
 
         // Supported options.
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH, DELETE_ON_CLOSE, CREATE_NEW,
-                TRUNCATE_EXISTING, SPARSE, SYNC, DSYNC)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), DELETE_ON_CLOSE,
+                CREATE_NEW, TRUNCATE_EXISTING, SPARSE, SYNC, DSYNC)) {
             assertEquals(TEST_FILE_DATA, readFromInputStream(is));
         }
     }
 
     @Test
     public void test_newInputStream_twice() throws IOException {
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH, READ);
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), READ);
              // Open the same file again.
-             InputStream is2 = provider.newInputStream(DATA_FILE_PATH, READ)) {
+             InputStream is2 = provider.newInputStream(filesSetup.getDataFilePath(), READ)) {
 
             assertEquals(TEST_FILE_DATA, readFromInputStream(is));
             assertEquals(TEST_FILE_DATA, readFromInputStream(is2));
@@ -142,25 +136,26 @@
             fail();
         } catch (NullPointerException expected) {}
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH, (OpenOption[]) null)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(),
+                (OpenOption[]) null)) {
             fail();
         } catch (NullPointerException expected) {}
     }
 
     @Test
     public void test_newOutputStream() throws IOException {
-        try (OutputStream os = provider.newOutputStream(TEST_PATH)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath())) {
             os.write(TEST_FILE_DATA.getBytes());
         }
 
-        try (InputStream is = provider.newInputStream(TEST_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) {
             assertEquals(TEST_FILE_DATA, readFromInputStream(is));
         }
     }
 
     @Test
     public void test_newOutputStream_openOption_READ() throws IOException {
-        try (OutputStream os = provider.newOutputStream(TEST_PATH, READ)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), READ)) {
             fail();
         } catch (IllegalArgumentException expected) {
         }
@@ -169,82 +164,83 @@
     @Test
     public void test_newOutputStream_openOption_APPEND() throws IOException {
         // When file exists and it contains data.
-        try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, APPEND)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), APPEND)) {
             os.write(TEST_FILE_DATA.getBytes());
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
             assertEquals(TEST_FILE_DATA + TEST_FILE_DATA, readFromInputStream(is));
         }
 
         // When file doesn't exist.
-        try (OutputStream os = provider.newOutputStream(TEST_PATH, APPEND)){
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), APPEND)) {
             fail();
         } catch (NoSuchFileException expected) {
-            assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+            assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
         }
     }
 
     @Test
     public void test_newOutputStream_openOption_TRUNCATE() throws IOException {
         // When file exists.
-        try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, TRUNCATE_EXISTING)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(),
+                TRUNCATE_EXISTING)) {
             os.write(TEST_FILE_DATA_2.getBytes());
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
             assertEquals(TEST_FILE_DATA_2, readFromInputStream(is));
         }
 
         // When file doesn't exist.
-        try (OutputStream os = provider.newOutputStream(TEST_PATH,
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(),
                 TRUNCATE_EXISTING)) {
             fail();
         } catch (NoSuchFileException expected) {
-            assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+            assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
         }
     }
 
     @Test
     public void test_newOutputStream_openOption_WRITE() throws IOException {
         // When file exists.
-        try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, WRITE)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), WRITE)) {
             os.write(TEST_FILE_DATA_2.getBytes());
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
             String expectedFileData = TEST_FILE_DATA_2 +
                     TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length());
             assertEquals(expectedFileData, readFromInputStream(is));
         }
 
         // When file doesn't exist.
-        try (OutputStream os = provider.newOutputStream(TEST_PATH, WRITE)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), WRITE)) {
             fail();
         } catch (NoSuchFileException expected) {
-            assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+            assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
         }
     }
 
     @Test
     public void test_newOutputStream_openOption_CREATE() throws IOException {
         // When file exists.
-        try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, CREATE)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), CREATE)) {
             os.write(TEST_FILE_DATA_2.getBytes());
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
             String expectedFileData = TEST_FILE_DATA_2 +
                     TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length());
             assertEquals(expectedFileData, readFromInputStream(is));
         }
 
         // When file doesn't exist.
-        try (OutputStream os = provider.newOutputStream(TEST_PATH, CREATE)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE)) {
             os.write(TEST_FILE_DATA.getBytes());
         }
 
-        try (InputStream is = provider.newInputStream(TEST_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) {
             assertEquals(TEST_FILE_DATA, readFromInputStream(is));
         }
     }
@@ -252,17 +248,17 @@
     @Test
     public void test_newOutputStream_openOption_CREATE_NEW() throws IOException {
         // When file exists.
-        try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, CREATE_NEW)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), CREATE_NEW)) {
             fail();
         } catch (FileAlreadyExistsException expected) {
         }
 
         // When file doesn't exist.
-        try (OutputStream os = provider.newOutputStream(TEST_PATH, CREATE_NEW)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE_NEW)) {
             os.write(TEST_FILE_DATA.getBytes());
         }
 
-        try (InputStream is = provider.newInputStream(TEST_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) {
             assertEquals(TEST_FILE_DATA, readFromInputStream(is));
         }
     }
@@ -270,8 +266,8 @@
     @Test
     public void test_newOutputStream_openOption_SYNC() throws IOException {
         // The data should be written to the file
-        try (OutputStream os = provider.newOutputStream(TEST_PATH, CREATE, SYNC);
-             InputStream is = provider.newInputStream(TEST_PATH, SYNC)) {
+        try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE, SYNC);
+             InputStream is = provider.newInputStream(filesSetup.getTestPath(), SYNC)) {
                 os.write(TEST_FILE_DATA.getBytes());
                 assertEquals(TEST_FILE_DATA, readFromInputStream(is));
         }
@@ -283,7 +279,8 @@
             fail();
         } catch (NullPointerException expected) {}
 
-        try (OutputStream os = provider.newOutputStream(TEST_PATH, (OpenOption[]) null)) {
+        try (OutputStream os = provider
+                .newOutputStream(filesSetup.getTestPath(), (OpenOption[]) null)) {
             fail();
         } catch (NullPointerException expected) {}
     }
@@ -293,23 +290,23 @@
         Set<OpenOption> set = new HashSet<OpenOption>();
 
         // When file doesn't exist
-        try (SeekableByteChannel sbc = provider.newByteChannel(TEST_PATH, set)) {
+        try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getTestPath(), set)) {
             fail();
         } catch (NoSuchFileException expected) {
-            assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+            assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
         }
 
         // When file exists.
 
         // File opens in READ mode by default. The channel is non writable by default.
-        try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+        try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
             sbc.write(ByteBuffer.allocate(10));
             fail();
         } catch (NonWritableChannelException expected) {
         }
 
         // Read a file.
-        try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+        try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
             ByteBuffer readBuffer = ByteBuffer.allocate(10);
             int bytesReadCount = sbc.read(readBuffer);
 
@@ -329,26 +326,26 @@
         set.add(WRITE);
 
         // When file doesn't exist
-        try (SeekableByteChannel sbc = provider.newByteChannel(TEST_PATH, set)) {
+        try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getTestPath(), set)) {
             fail();
         } catch (NoSuchFileException expected) {
-            assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+            assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
         }
 
 
         // When file exists.
-        try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+        try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
             sbc.read(ByteBuffer.allocate(10));
             fail();
         } catch (NonReadableChannelException expected) {
         }
 
         // Write in file.
-        try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+        try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
             sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
             String expectedFileData = TEST_FILE_DATA_2 +
                     TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length());
             assertEquals(expectedFileData, readFromInputStream(is));
@@ -366,7 +363,7 @@
         set.add(READ);
         set.add(SYNC);
 
-        try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+        try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
             ByteBuffer readBuffer = ByteBuffer.allocate(10);
             int bytesReadCount = sbc.read(readBuffer);
 
@@ -379,7 +376,7 @@
             sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
         }
 
-        try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+        try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
             String expectedFileData = TEST_FILE_DATA + TEST_FILE_DATA_2;
             assertEquals(expectedFileData, readFromInputStream(is));
         }
@@ -392,7 +389,8 @@
             fail();
         } catch(NullPointerException expected) {}
 
-        try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, null)) {
+        try (SeekableByteChannel sbc = provider
+                .newByteChannel(filesSetup.getDataFilePath(), null)) {
             fail();
         } catch(NullPointerException expected) {}
     }
@@ -400,7 +398,7 @@
     @Test
     public void test_createDirectory() throws IOException {
         // Check if createDirectory is actually creating a directory.
-        Path newDirectory = Paths.get(TEST_DIR, "newDir");
+        Path newDirectory = Paths.get(filesSetup.getTestDir(), "newDir");
         assertFalse(Files.exists(newDirectory));
         assertFalse(Files.isDirectory(newDirectory));
 
@@ -417,7 +415,7 @@
         }
 
         // File with unicode name.
-        Path unicodeFilePath = Paths.get(TEST_DIR, "टेस्ट डायरेक्टरी");
+        Path unicodeFilePath = Paths.get(filesSetup.getTestDir(), "टेस्ट डायरेक्टरी");
         provider.createDirectory(unicodeFilePath);
         assertTrue(Files.exists(unicodeFilePath));
     }
@@ -426,13 +424,13 @@
     public void test_createDirectory$String$FileAttr() throws IOException {
         Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
         FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
-        provider.createDirectory(TEST_PATH, attr);
-        assertEquals(attr.value(), Files.getAttribute(TEST_PATH, attr.name()));
+        provider.createDirectory(filesSetup.getTestPath(), attr);
+        assertEquals(attr.value(), Files.getAttribute(filesSetup.getTestPath(), attr.name()));
 
         // Creating a new file and passing multiple attribute of the same name.
         perm = PosixFilePermissions.fromString("rw-------");
         FileAttribute<Set<PosixFilePermission>> attr1 = PosixFilePermissions.asFileAttribute(perm);
-        Path dirPath2 = Paths.get(TEST_DIR, "new_file");
+        Path dirPath2 = Paths.get(filesSetup.getTestDir(), "new_file");
         provider.createDirectory(dirPath2, attr, attr1);
         // Value should be equal to the last attribute passed.
         assertEquals(attr1.value(), Files.getAttribute(dirPath2, attr.name()));
@@ -448,7 +446,7 @@
         } catch(NullPointerException expected) {}
 
         try {
-            provider.createDirectory(TEST_PATH, (FileAttribute<?>[]) null);
+            provider.createDirectory(filesSetup.getTestPath(), (FileAttribute<?>[]) null);
             fail();
         } catch(NullPointerException expected) {}
     }
@@ -463,34 +461,37 @@
 
     @Test
     public void test_createSymbolicLink() throws IOException {
-        provider.createSymbolicLink(/* Path of the symbolic link */ TEST_PATH,
-                /* Path of the target of the symbolic link */ DATA_FILE_PATH.toAbsolutePath());
-        assertTrue(Files.isSymbolicLink(TEST_PATH));
+        provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(),
+                /* Path of the target of the symbolic link */
+                filesSetup.getDataFilePath().toAbsolutePath());
+        assertTrue(Files.isSymbolicLink(filesSetup.getTestPath()));
 
         // When file exists at the sym link location.
         try {
-            provider.createSymbolicLink(/* Path of the symbolic link */ TEST_PATH,
-                    /* Path of the target of the symbolic link */ DATA_FILE_PATH.toAbsolutePath());
+            provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(),
+                    /* Path of the target of the symbolic link */
+                    filesSetup.getDataFilePath().toAbsolutePath());
             fail();
         } catch (FileAlreadyExistsException expected) {} finally {
-            Files.deleteIfExists(TEST_PATH);
+            Files.deleteIfExists(filesSetup.getTestPath());
         }
 
         // Sym link to itself
-        provider.createSymbolicLink(/* Path of the symbolic link */ TEST_PATH,
-                /* Path of the target of the symbolic link */ TEST_PATH.toAbsolutePath());
-        assertTrue(Files.isSymbolicLink(TEST_PATH.toAbsolutePath()));
+        provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(),
+                /* Path of the target of the symbolic link */
+                filesSetup.getTestPath().toAbsolutePath());
+        assertTrue(Files.isSymbolicLink(filesSetup.getTestPath().toAbsolutePath()));
     }
 
     @Test
     public void test_createSymbolicLink_NPE() throws IOException {
         try {
-            provider.createSymbolicLink(null, DATA_FILE_PATH.toAbsolutePath());
+            provider.createSymbolicLink(null, filesSetup.getDataFilePath().toAbsolutePath());
             fail();
         } catch (NullPointerException expected) {}
 
         try {
-            provider.createSymbolicLink(TEST_PATH, null);
+            provider.createSymbolicLink(filesSetup.getTestPath(), null);
             fail();
         } catch (NullPointerException expected) {}
     }
@@ -501,8 +502,8 @@
             Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
             FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions
                     .asFileAttribute(perm);
-            provider.createSymbolicLink(TEST_PATH, DATA_FILE_PATH.toAbsolutePath()
-                    , attr);
+            provider.createSymbolicLink(filesSetup.getTestPath(),
+                    filesSetup.getDataFilePath().toAbsolutePath(), attr);
             fail();
         } catch (UnsupportedOperationException expected) {}
     }
@@ -514,18 +515,18 @@
                 .asFileAttribute(perm);
 
         try {
-            provider.createSymbolicLink(null, DATA_FILE_PATH.toAbsolutePath(), attr);
+            provider.createSymbolicLink(null, filesSetup.getDataFilePath().toAbsolutePath(), attr);
             fail();
         } catch (NullPointerException expected) {}
 
         try {
-            provider.createSymbolicLink(TEST_PATH, null, attr);
+            provider.createSymbolicLink(filesSetup.getTestPath(), null, attr);
             fail();
 
         } catch (NullPointerException expected) {}
 
         try {
-            provider.createSymbolicLink(TEST_PATH, DATA_FILE_PATH,
+            provider.createSymbolicLink(filesSetup.getTestPath(), filesSetup.getDataFilePath(),
                     (FileAttribute<?>[]) null);
             fail();
         } catch (NullPointerException expected) {}
@@ -534,19 +535,19 @@
     @Test
     public void test_delete() throws IOException {
         // Delete existing file.
-        provider.delete(DATA_FILE_PATH);
-        assertFalse(Files.exists(DATA_FILE_PATH));
+        provider.delete(filesSetup.getDataFilePath());
+        assertFalse(Files.exists(filesSetup.getDataFilePath()));
 
         // Delete non existing files.
         try {
-            provider.delete(TEST_PATH);
+            provider.delete(filesSetup.getTestPath());
             fail();
         } catch (NoSuchFileException expected) {
-            assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+            assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
         }
 
         // Delete a directory.
-        Path dirPath = Paths.get(TEST_DIR, "dir");
+        Path dirPath = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectory(dirPath);
         provider.delete(dirPath);
         assertFalse(Files.exists(dirPath));
@@ -554,7 +555,7 @@
 
         // Delete a non empty directory.
         Files.createDirectory(dirPath);
-        Files.createFile(Paths.get(TEST_DIR, "dir/file"));
+        Files.createFile(Paths.get(filesSetup.getTestDir(), "dir/file"));
         try {
             provider.delete(dirPath);
             fail();
@@ -572,21 +573,21 @@
     @Test
     public void test_deleteIfExist() throws IOException {
         // Delete existing file.
-        assertTrue(Files.deleteIfExists(DATA_FILE_PATH));
-        assertFalse(Files.exists(DATA_FILE_PATH));
+        assertTrue(Files.deleteIfExists(filesSetup.getDataFilePath()));
+        assertFalse(Files.exists(filesSetup.getDataFilePath()));
 
         // Delete non existing files.
-        assertFalse(Files.deleteIfExists(TEST_PATH));
+        assertFalse(Files.deleteIfExists(filesSetup.getTestPath()));
 
         // Delete a directory.
-        Path dirPath = Paths.get(TEST_DIR, "dir");
+        Path dirPath = Paths.get(filesSetup.getTestDir(), "dir");
         Files.createDirectory(dirPath);
         assertTrue(Files.deleteIfExists(dirPath));
         assertFalse(Files.exists(dirPath));
 
         // Delete a non empty directory.
         Files.createDirectory(dirPath);
-        Files.createFile(Paths.get(TEST_DIR, "dir/file"));
+        Files.createFile(Paths.get(filesSetup.getTestDir(), "dir/file"));
         try {
             provider.deleteIfExists(dirPath);
             fail();
@@ -603,63 +604,64 @@
 
     @Test
     public void test_copy() throws IOException {
-        provider.copy(DATA_FILE_PATH, TEST_PATH);
-        assertEquals(TEST_FILE_DATA, readFromFile(TEST_PATH));
+        provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath());
+        assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath()));
         // The original file should also exists.
-        assertEquals(TEST_FILE_DATA, readFromFile(DATA_FILE_PATH));
+        assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getDataFilePath()));
 
         // When target file exists.
         try {
-            provider.copy(DATA_FILE_PATH, TEST_PATH);
+            provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath());
             fail();
         } catch (FileAlreadyExistsException expected) {}
 
         // Copy to existing target file with REPLACE_EXISTING copy option.
-        writeToFile(DATA_FILE_PATH, TEST_FILE_DATA_2);
-        provider.copy(DATA_FILE_PATH, TEST_PATH, REPLACE_EXISTING);
-        assertEquals(TEST_FILE_DATA_2, readFromFile(TEST_PATH));
+        writeToFile(filesSetup.getDataFilePath(), TEST_FILE_DATA_2);
+        provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), REPLACE_EXISTING);
+        assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getTestPath()));
 
 
         // Copy to the same file. Should not fail.
-        reset();
-        provider.copy(DATA_FILE_PATH, DATA_FILE_PATH);
-        assertEquals(TEST_FILE_DATA, readFromFile(DATA_FILE_PATH));
+        filesSetup.reset();
+        provider.copy(filesSetup.getDataFilePath(), filesSetup.getDataFilePath());
+        assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getDataFilePath()));
 
         // With target is a symbolic link file.
         try {
-            reset();
-            Path symlink = Paths.get(TEST_DIR, "symlink");
-            Path newFile = Paths.get(TEST_DIR, "newDir");
+            filesSetup.reset();
+            Path symlink = Paths.get(filesSetup.getTestDir(), "symlink");
+            Path newFile = Paths.get(filesSetup.getTestDir(), "newDir");
             Files.createFile(newFile);
             assertTrue(Files.exists(newFile));
-            Files.createSymbolicLink(symlink, DATA_FILE_PATH);
-            provider.copy(DATA_FILE_PATH, symlink);
+            Files.createSymbolicLink(symlink, filesSetup.getDataFilePath());
+            provider.copy(filesSetup.getDataFilePath(), symlink);
             fail();
         } catch (FileAlreadyExistsException expected) {}
 
-        reset();
+        filesSetup.reset();
         try {
-            provider.copy(TEST_PATH, DATA_FILE_PATH, REPLACE_EXISTING);
+            provider.copy(filesSetup.getTestPath(), filesSetup.getDataFilePath(), REPLACE_EXISTING);
             fail();
         } catch (NoSuchFileException expected) {
-            assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+            assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
         }
     }
 
     @Test
     public void test_copy_NPE() throws IOException {
         try {
-            provider.copy((Path)null, TEST_PATH);
+            provider.copy((Path) null, filesSetup.getTestPath());
             fail();
         } catch(NullPointerException expected) {}
 
         try {
-            provider.copy(DATA_FILE_PATH, (Path)null);
+            provider.copy(filesSetup.getDataFilePath(), (Path) null);
             fail();
         } catch(NullPointerException expected) {}
 
         try {
-            provider.copy(DATA_FILE_PATH, TEST_PATH, (CopyOption[]) null);
+            provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(),
+                    (CopyOption[]) null);
             fail();
         } catch(NullPointerException expected) {}
     }
@@ -668,39 +670,41 @@
     public void test_copy_CopyOption() throws IOException {
         // COPY_ATTRIBUTES
         FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis() - 10000);
-        Files.setAttribute(DATA_FILE_PATH, "basic:lastModifiedTime", fileTime);
-        provider.copy(DATA_FILE_PATH, TEST_PATH, COPY_ATTRIBUTES);
+        Files.setAttribute(filesSetup.getDataFilePath(), "basic:lastModifiedTime", fileTime);
+        provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), COPY_ATTRIBUTES);
         assertEquals(fileTime.to(TimeUnit.SECONDS),
-                ((FileTime)Files.getAttribute(TEST_PATH,
+                ((FileTime) Files.getAttribute(filesSetup.getTestPath(),
                         "basic:lastModifiedTime")).to(TimeUnit.SECONDS));
-        assertEquals(TEST_FILE_DATA, readFromFile(TEST_PATH));
+        assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath()));
 
         // ATOMIC_MOVE
-        Files.deleteIfExists(TEST_PATH);
+        Files.deleteIfExists(filesSetup.getTestPath());
         try {
-            provider.copy(DATA_FILE_PATH, TEST_PATH, ATOMIC_MOVE);
+            provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), ATOMIC_MOVE);
             fail();
         } catch (UnsupportedOperationException expected) {}
 
-        Files.deleteIfExists(TEST_PATH);
+        Files.deleteIfExists(filesSetup.getTestPath());
         try {
-            provider.copy(DATA_FILE_PATH, TEST_PATH, NonStandardOption.OPTION1);
+            provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(),
+                    NonStandardOption.OPTION1);
             fail();
         } catch (UnsupportedOperationException expected) {}
     }
 
     @Test
     public void test_copy_directory() throws IOException {
-        final Path dirPath = Paths.get(TEST_DIR, "dir1");
-        final Path dirPath2 = Paths.get(TEST_DIR, "dir2");
+        final Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1");
+        final Path dirPath2 = Paths.get(filesSetup.getTestDir(), "dir2");
         // Nested directory.
-        final Path dirPath3 = Paths.get(TEST_DIR, "dir1/dir");
+        final Path dirPath3 = Paths.get(filesSetup.getTestDir(), "dir1/dir");
 
         // Create dir1 and dir1/dir, and copying dir1/dir to dir2. Copy will create dir2, however,
         // it will not copy the content of the source directory.
         Files.createDirectory(dirPath);
         Files.createDirectory(dirPath3);
-        provider.copy(DATA_FILE_PATH, Paths.get(TEST_DIR, "dir1/" + DATA_FILE));
+        provider.copy(filesSetup.getDataFilePath(),
+                Paths.get(filesSetup.getTestDir(), "dir1/" + DATA_FILE));
         provider.copy(dirPath, dirPath2);
         assertTrue(Files.exists(dirPath2));
 
@@ -714,7 +718,7 @@
 
 
         // When the target directory is not empty.
-        Path dirPath4 = Paths.get(TEST_DIR, "dir4");
+        Path dirPath4 = Paths.get(filesSetup.getTestDir(), "dir4");
         Files.createDirectories(dirPath4);
         Path file = Paths.get("file");
         Files.createFile(Paths.get(dirPath.toString(), file.toString()));
@@ -730,14 +734,14 @@
     public void test_newDirectoryStream$Path$Filter() throws IOException {
 
         // Initial setup of directory.
-        Path path_root = Paths.get(TEST_DIR, "dir");
-        Path path_dir1 = Paths.get(TEST_DIR, "dir/dir1");
-        Path path_dir2 = Paths.get(TEST_DIR, "dir/dir2");
-        Path path_dir3 = Paths.get(TEST_DIR, "dir/dir3");
+        Path path_root = Paths.get(filesSetup.getTestDir(), "dir");
+        Path path_dir1 = Paths.get(filesSetup.getTestDir(), "dir/dir1");
+        Path path_dir2 = Paths.get(filesSetup.getTestDir(), "dir/dir2");
+        Path path_dir3 = Paths.get(filesSetup.getTestDir(), "dir/dir3");
 
-        Path path_f1 = Paths.get(TEST_DIR, "dir/f1");
-        Path path_f2 = Paths.get(TEST_DIR, "dir/f2");
-        Path path_f3 = Paths.get(TEST_DIR, "dir/f3");
+        Path path_f1 = Paths.get(filesSetup.getTestDir(), "dir/f1");
+        Path path_f2 = Paths.get(filesSetup.getTestDir(), "dir/f2");
+        Path path_f3 = Paths.get(filesSetup.getTestDir(), "dir/f3");
 
         Files.createDirectory(path_root);
         Files.createDirectory(path_dir1);
@@ -772,7 +776,7 @@
     @Test
     public void test_newDirectoryStream$Filter_Exception() throws IOException {
         // Non existent directory.
-        Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+        Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
         DirectoryStream.Filter<Path> fileFilter = new DirectoryStream.Filter<Path>() {
             @Override
             public boolean accept(Path entry) throws IOException {
@@ -788,7 +792,7 @@
         }
 
         // File instead of directory.
-        Path path_file1 = Paths.get(TEST_DIR, "newFile1");
+        Path path_file1 = Paths.get(filesSetup.getTestDir(), "newFile1");
         Files.createFile(path_file1);
         try (DirectoryStream<Path> directoryStream = provider.newDirectoryStream(path_file1,
                 fileFilter)) {
@@ -812,7 +816,7 @@
         }
 
         // Non existent directory.
-        Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+        Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
         try (DirectoryStream<Path> directoryStream = provider.newDirectoryStream(path_dir1,
                 (DirectoryStream.Filter<? super Path>) null)) {
             fail();
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 c845cc3..6931978 100644
--- a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
+++ b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
@@ -35,9 +35,7 @@
 import java.nio.file.Paths;
 import java.nio.file.attribute.BasicFileAttributes;
 
-public class FilesSetup implements TestRule {
-
-    final static String TEST_DIR = "testDir";
+class FilesSetup implements TestRule {
 
     final static String DATA_FILE = "dataFile";
 
@@ -47,19 +45,28 @@
 
     final static String TEST_FILE_DATA_2 = "test";
 
-    final static Path DATA_FILE_PATH = Paths.get(TEST_DIR, DATA_FILE);
+    private String testDir;
 
-    final static Path TEST_PATH = Paths.get(TEST_DIR, NON_EXISTENT_FILE);
+    private Path dataFilePath;
 
-    final static Path TEST_DIR_PATH = Paths.get(TEST_DIR);
+    private Path testPath;
 
-    public void setUp() throws Exception {
+    private boolean filesInitialized = false;
+
+    void setUp() throws Exception {
         initializeFiles();
     }
 
-    private static void initializeFiles() throws IOException {
-        Files.createDirectory(TEST_DIR_PATH);
-        File testInputFile = new File(TEST_DIR, DATA_FILE);
+    void tearDown() throws Exception {
+        filesInitialized = false;
+        clearAll();
+    }
+
+    private void initializeFiles() throws IOException {
+        testDir = Files.createTempDirectory("testDir").toString();
+        dataFilePath = Paths.get(testDir, DATA_FILE);
+        testPath = Paths.get(testDir, NON_EXISTENT_FILE);
+        File testInputFile = new File(testDir, DATA_FILE);
         if (!testInputFile.exists()) {
             testInputFile.createNewFile();
         }
@@ -67,18 +74,36 @@
         BufferedWriter bw = new BufferedWriter(fw);
         bw.write(TEST_FILE_DATA);
         bw.close();
+        filesInitialized = true;
     }
 
-    static public void tearDown() throws Exception {
-        clearAll();
+    Path getTestPath() {
+        checkState();
+        return testPath;
     }
 
-    static void clearAll() throws IOException {
-        Path root = Paths.get(TEST_DIR);
+    Path getDataFilePath() {
+        checkState();
+        return dataFilePath;
+    }
+
+    String getTestDir() {
+        checkState();
+        return testDir;
+    }
+
+    private void checkState() {
+        if (!filesInitialized) {
+            throw new IllegalStateException("Files are not setup.");
+        }
+    }
+
+    void clearAll() throws IOException {
+        Path root = Paths.get(testDir);
         delete(root);
     }
 
-    static void reset() throws IOException {
+    void reset() throws IOException {
         clearAll();
         initializeFiles();
     }
diff --git a/luni/src/test/java/libcore/java/nio/file/FilesTest.java b/luni/src/test/java/libcore/java/nio/file/FilesTest.java
index 6a7740a..3f967c9 100644
--- a/luni/src/test/java/libcore/java/nio/file/FilesTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/FilesTest.java
@@ -19,13 +19,9 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.Statement;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
-import org.mockito.runners.MockitoJUnitRunner;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -53,9 +49,6 @@
 
 import static java.nio.file.StandardOpenOption.APPEND;
 import static java.nio.file.StandardOpenOption.READ;
-import static libcore.java.nio.file.FilesSetup.DATA_FILE_PATH;
-import static libcore.java.nio.file.FilesSetup.TEST_DIR;
-import static libcore.java.nio.file.FilesSetup.TEST_PATH;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
@@ -115,7 +108,7 @@
 
     @Test
     public void test_newByteChannel() throws IOException {
-        try (FileChannel sfc = FileChannel.open(DATA_FILE_PATH)) {
+        try (FileChannel sfc = FileChannel.open(filesSetup.getDataFilePath())) {
             HashSet<OpenOption> openOptions = new HashSet<>();
             openOptions.add(READ);
 
@@ -129,18 +122,18 @@
 
     @Test
     public void test_createFile() throws IOException {
-        assertFalse(Files.exists(TEST_PATH));
-        Files.createFile(TEST_PATH);
-        assertTrue(Files.exists(TEST_PATH));
+        assertFalse(Files.exists(filesSetup.getTestPath()));
+        Files.createFile(filesSetup.getTestPath());
+        assertTrue(Files.exists(filesSetup.getTestPath()));
 
         // File with unicode name.
-        Path unicodeFilePath = Paths.get(TEST_DIR, "परीक्षण फ़ाइल");
+        Path unicodeFilePath = Paths.get(filesSetup.getTestDir(), "परीक्षण फ़ाइल");
         Files.createFile(unicodeFilePath);
         Files.exists(unicodeFilePath);
 
         // When file exists.
         try {
-            Files.createFile(DATA_FILE_PATH);
+            Files.createFile(filesSetup.getDataFilePath());
             fail();
         } catch(FileAlreadyExistsException expected) {}
     }
@@ -157,20 +150,20 @@
     public void test_createFile$String$Attr() throws IOException {
         Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
         FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
-        Files.createFile(TEST_PATH, attr);
-        assertEquals(attr.value(), Files.getAttribute(TEST_PATH, attr.name()));
+        Files.createFile(filesSetup.getTestPath(), attr);
+        assertEquals(attr.value(), Files.getAttribute(filesSetup.getTestPath(), attr.name()));
 
         // Creating a new file and passing multiple attribute of the same name.
         perm = PosixFilePermissions.fromString("rw-------");
         FileAttribute<Set<PosixFilePermission>> attr1 = PosixFilePermissions.asFileAttribute(perm);
-        Path filePath2 = Paths.get(TEST_DIR, "new_file");
+        Path filePath2 = Paths.get(filesSetup.getTestDir(), "new_file");
         Files.createFile(filePath2, attr, attr1);
         // Value should be equal to the last attribute passed.
         assertEquals(attr1.value(), Files.getAttribute(filePath2, attr.name()));
 
         // When file exists.
         try {
-            Files.createFile(DATA_FILE_PATH, attr);
+            Files.createFile(filesSetup.getDataFilePath(), attr);
             fail();
         } catch(FileAlreadyExistsException expected) {}
     }
@@ -186,7 +179,7 @@
     @Test
     public void test_createDirectories() throws IOException {
         // Should be able to create parent directories.
-        Path dirPath = Paths.get(TEST_DIR, "dir1/dir2/dir3");
+        Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir3");
         assertFalse(Files.exists(dirPath));
         Files.createDirectories(dirPath);
         assertTrue(Files.isDirectory(dirPath));
@@ -205,7 +198,7 @@
 
     @Test
     public void test_createDirectories$Path$Attr() throws IOException {
-        Path dirPath = Paths.get(TEST_DIR, "dir1/dir2/dir3");
+        Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir3");
         Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
         FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
         assertFalse(Files.exists(dirPath));
@@ -221,7 +214,7 @@
         assertEquals(attr.value(), Files.getAttribute(dirPath, attr.name()));
 
         // Creating a new directory and passing multiple attribute of the same name.
-        Path dirPath2 = Paths.get(TEST_DIR, "dir1/dir2/dir4");
+        Path dirPath2 = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir4");
         Files.createDirectories(dirPath2, attr, attr1);
         // Value should be equal to the last attribute passed.
         assertEquals(attr1.value(), Files.getAttribute(dirPath2, attr.name()));
@@ -229,7 +222,7 @@
 
     @Test
     public void test_createDirectories$Path$Attr_NPE() throws IOException {
-        Path dirPath = Paths.get(TEST_DIR, "dir1/dir2/dir3");
+        Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir3");
         Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
         FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
         try {
@@ -246,12 +239,12 @@
     @Test
     public void test_newDirectoryStream() throws IOException {
         // Directory setup.
-        Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
-        Path path_dir2 = Paths.get(TEST_DIR, "newDir1/newDir2");
-        Path path_dir3 = Paths.get(TEST_DIR, "newDir1/newDir3");
-        Path path_file1 = Paths.get(TEST_DIR, "newDir1/newFile1");
-        Path path_file2 = Paths.get(TEST_DIR, "newDir1/newFile2");
-        Path path_file3 = Paths.get(TEST_DIR, "newDir1/newDir2/newFile3");
+        Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
+        Path path_dir2 = Paths.get(filesSetup.getTestDir(), "newDir1/newDir2");
+        Path path_dir3 = Paths.get(filesSetup.getTestDir(), "newDir1/newDir3");
+        Path path_file1 = Paths.get(filesSetup.getTestDir(), "newDir1/newFile1");
+        Path path_file2 = Paths.get(filesSetup.getTestDir(), "newDir1/newFile2");
+        Path path_file3 = Paths.get(filesSetup.getTestDir(), "newDir1/newDir2/newFile3");
 
         Files.createDirectory(path_dir1);
         Files.createDirectory(path_dir2);
@@ -277,14 +270,14 @@
     public void test_newDirectoryStream_Exception() throws IOException {
 
         // Non existent directory.
-        Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+        Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
         try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_dir1)) {
             fail();
         } catch (NoSuchFileException expected) {
         }
 
         // File instead of directory.
-        Path path_file1 = Paths.get(TEST_DIR, "newFile1");
+        Path path_file1 = Paths.get(filesSetup.getTestDir(), "newFile1");
         Files.createFile(path_file1);
         try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_file1)) {
             fail();
@@ -300,14 +293,14 @@
     @Test
     public void test_newDirectoryStream$Path$String() throws IOException {
         // Directory setup.
-        Path path_root = Paths.get(TEST_DIR, "dir");
-        Path path_java1 = Paths.get(TEST_DIR, "dir/f1.java");
-        Path path_java2 = Paths.get(TEST_DIR, "dir/f2.java");
-        Path path_java3 = Paths.get(TEST_DIR, "dir/f3.java");
+        Path path_root = Paths.get(filesSetup.getTestDir(), "dir");
+        Path path_java1 = Paths.get(filesSetup.getTestDir(), "dir/f1.java");
+        Path path_java2 = Paths.get(filesSetup.getTestDir(), "dir/f2.java");
+        Path path_java3 = Paths.get(filesSetup.getTestDir(), "dir/f3.java");
 
-        Path path_txt1 = Paths.get(TEST_DIR, "dir/f1.txt");
-        Path path_txt2 = Paths.get(TEST_DIR, "dir/f2.txt");
-        Path path_txt3 = Paths.get(TEST_DIR, "dir/f3.txt");
+        Path path_txt1 = Paths.get(filesSetup.getTestDir(), "dir/f1.txt");
+        Path path_txt2 = Paths.get(filesSetup.getTestDir(), "dir/f2.txt");
+        Path path_txt3 = Paths.get(filesSetup.getTestDir(), "dir/f3.txt");
 
         Files.createDirectory(path_root);
         // A directory with .java extension.
@@ -335,14 +328,14 @@
     public void test_newDirectoryStream$Path$String_Exception() throws IOException {
 
         // Non existent directory.
-        Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+        Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
         try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_dir1, "*.c")) {
             fail();
         } catch (NoSuchFileException expected) {
         }
 
         // File instead of directory.
-        Path path_file1 = Paths.get(TEST_DIR, "newFile1");
+        Path path_file1 = Paths.get(filesSetup.getTestDir(), "newFile1");
         Files.createFile(path_file1);
         try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_file1, "*.c")) {
             fail();
diff --git a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java b/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
index 74d8473..397108c 100644
--- a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
+++ b/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
@@ -22,7 +22,8 @@
 
     public void test_getOrDefault() {
         MapDefaultMethodTester.test_getOrDefault(new ConcurrentHashMap<>(),
-                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
+                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/,
+                true /*getAcceptsAnyObject*/);
     }
 
     public void test_forEach() {
diff --git a/luni/src/test/java/libcore/java/util/HashMapTest.java b/luni/src/test/java/libcore/java/util/HashMapTest.java
index 12bf59b..d4585b7 100644
--- a/luni/src/test/java/libcore/java/util/HashMapTest.java
+++ b/luni/src/test/java/libcore/java/util/HashMapTest.java
@@ -16,14 +16,25 @@
 
 package libcore.java.util;
 
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.TreeMap;
 
 public class HashMapTest extends junit.framework.TestCase {
 
     public void test_getOrDefault() {
         MapDefaultMethodTester.test_getOrDefault(new HashMap<>(), true /*acceptsNullKey*/,
-                true /*acceptsNullValue*/);
+                true /*acceptsNullValue*/, true /*getAcceptsAnyObject*/);
     }
 
     public void test_forEach() {
@@ -70,6 +81,146 @@
                 .test_merge(new HashMap<>(), true /*acceptsNullKey*/);
     }
 
+    public void test_spliterator_keySet() {
+        Map<String, Integer> m = new HashMap<>();
+        m.put("a", 1);
+        m.put("b", 2);
+        m.put("c", 3);
+        m.put("d", 4);
+        m.put("e", 5);
+        m.put("f", 6);
+        m.put("g", 7);
+        m.put("h", 8);
+        m.put("i", 9);
+        m.put("j", 10);
+        ArrayList<String> expectedKeys = new ArrayList<>(
+                Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"));
+        Set<String> keys = m.keySet();
+        SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys);
+        SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+        SpliteratorTester.runSizedTests(keys.spliterator(), 10);
+        assertEquals(Spliterator.DISTINCT | Spliterator.SIZED,
+                keys.spliterator().characteristics());
+    }
+
+    public void test_spliterator_values() {
+        Map<String, Integer> m = new HashMap<>();
+        m.put("a", 1);
+        m.put("b", 2);
+        m.put("c", 3);
+        m.put("d", 4);
+        m.put("e", 5);
+        m.put("f", 6);
+        m.put("g", 7);
+        m.put("h", 8);
+        m.put("i", 9);
+        m.put("j", 10);
+        ArrayList<Integer> expectedValues = new ArrayList<>(
+                Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+        );
+        Collection<Integer> values = m.values();
+        SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+        SpliteratorTester.runSizedTests(values, 10);
+        assertEquals(Spliterator.SIZED, values.spliterator().characteristics());
+    }
+
+    public void test_spliterator_entrySet() {
+        MapDefaultMethodTester.test_entrySet_spliterator_unordered(new HashMap<>());
+
+        Map<String, Integer> m = new HashMap<>(Collections.singletonMap("key", 42));
+        assertEquals(Spliterator.DISTINCT | Spliterator.SIZED,
+                m.entrySet().spliterator().characteristics());
+    }
+
+    /**
+     * Checks that {@code HashMap.entrySet().spliterator().trySplit()}
+     * estimates half of the parents' estimate (rounded down, which
+     * can be an underestimate) but is not itself SIZED.
+     *
+     * These assertions are still stronger than what the documentation
+     * guarantees since un-SIZED Spliterators' size estimates may be off by
+     * an arbitrary amount.
+     */
+    public void test_entrySet_subsizeEstimates() {
+        Map<String, String> m = new HashMap<>();
+        assertNull(m.entrySet().spliterator().trySplit());
+        // For the empty map, the estimates are exact
+        assertEquals(0, m.entrySet().spliterator().estimateSize());
+        assertEquals(0, m.entrySet().spliterator().getExactSizeIfKnown());
+
+        m.put("key1", "value1");
+        assertSubsizeEstimate(m.entrySet().spliterator(), 0);
+        m.put("key2", "value2");
+        assertSubsizeEstimate(m.entrySet().spliterator(), 1);
+        m.put("key3", "value3");
+        m.put("key4", "value4");
+        m.put("key5", "value5");
+        m.put("key6", "value6");
+        m.put("key7", "value7");
+        m.put("key8", "value8");
+        assertSubsizeEstimate(m.entrySet().spliterator(), 4);
+
+        m.put("key9", "value9");
+        assertSubsizeEstimate(m.entrySet().spliterator(), 4);
+        assertFalse(m.entrySet().spliterator().trySplit().hasCharacteristics(Spliterator.SIZED));
+    }
+
+    /**
+     * Checks that HashMap.entrySet()'s spliterator halfs its estimate (rounding down)
+     * for each split, even though this estimate may be inaccurate.
+     */
+    public void test_entrySet_subsizeEstimates_recursive() {
+        Map<Integer, String> m = new HashMap<>();
+        for (int i = 0; i < 100; i++) {
+            m.put(i, "value");
+        }
+        Set<Map.Entry<Integer, String>> entries = m.entrySet();
+        // Recursive splitting - HashMap will estimate the size halving each split, rounding down.
+        assertSubsizeEstimate(entries.spliterator(), 50);
+        assertSubsizeEstimate(entries.spliterator().trySplit(), 25);
+        assertSubsizeEstimate(entries.spliterator().trySplit().trySplit(), 12);
+        assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit(), 6);
+        assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit().trySplit(), 3);
+        assertSubsizeEstimate(
+                entries.spliterator().trySplit().trySplit().trySplit().trySplit().trySplit(), 1);
+        assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit().trySplit()
+                .trySplit().trySplit(), 0);
+    }
+
+    /**
+     * Checks that HashMap.EntryIterator is SIZED but not SUBSIZED.
+     */
+    public void test_entrySet_spliterator_sizedButNotSubsized() {
+        Map<String, String> m = new HashMap<>();
+        assertTrue(m.entrySet().spliterator().hasCharacteristics(Spliterator.SIZED));
+        assertFalse(m.entrySet().spliterator().hasCharacteristics(Spliterator.SUBSIZED));
+        m.put("key1", "value1");
+        m.put("key2", "value2");
+        assertTrue(m.entrySet().spliterator().hasCharacteristics(Spliterator.SIZED));
+        assertFalse(m.entrySet().spliterator().hasCharacteristics(Spliterator.SUBSIZED));
+        Spliterator<Map.Entry<String, String>> parent = m.entrySet().spliterator();
+        Spliterator<Map.Entry<String, String>> child = parent.trySplit();
+        assertFalse(parent.hasCharacteristics(Spliterator.SIZED));
+        assertFalse(child.hasCharacteristics(Spliterator.SIZED));
+        assertFalse(parent.hasCharacteristics(Spliterator.SUBSIZED));
+        assertFalse(child.hasCharacteristics(Spliterator.SUBSIZED));
+    }
+
+    /**
+     * Tests that the given spliterator can be trySplit(), resulting in children that each
+     * estimate the specified size.
+     */
+    private static<T> void assertSubsizeEstimate(Spliterator<T> spliterator,
+            long expectedEstimate) {
+        Spliterator<T> child = spliterator.trySplit();
+        assertNotNull(child);
+        assertEquals(expectedEstimate, spliterator.estimateSize());
+        assertEquals(expectedEstimate, child.estimateSize());
+    }
+
     public void test_replaceAll() throws Exception {
         HashMap<String, String> map = new HashMap<>();
         map.put("one", "1");
@@ -83,12 +234,9 @@
         assertEquals(3, map.size());
 
         try {
-            map.replaceAll(new java.util.function.BiFunction<String, String, String>() {
-                @Override
-                public String apply(String k, String v) {
-                    map.put("foo1", v);
-                    return v;
-                }
+            map.replaceAll((k, v) -> {
+                map.put("foo1", v);
+                return v;
             });
             fail();
         } catch(ConcurrentModificationException expected) {}
diff --git a/luni/src/test/java/libcore/java/util/HashtableTest.java b/luni/src/test/java/libcore/java/util/HashtableTest.java
index 40fbf25..a7650e2 100644
--- a/luni/src/test/java/libcore/java/util/HashtableTest.java
+++ b/luni/src/test/java/libcore/java/util/HashtableTest.java
@@ -24,7 +24,7 @@
 
     public void test_getOrDefault() {
         MapDefaultMethodTester.test_getOrDefault(new Hashtable<>(), false /*doesNotAcceptNullKey*/,
-                false /*doesNotAcceptNullValue*/);
+                false /*doesNotAcceptNullValue*/, true /*getAcceptsAnyObject*/);
     }
 
     public void test_forEach() {
diff --git a/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java b/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java
index fec95b1..e25f8fe 100644
--- a/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java
+++ b/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java
@@ -16,9 +16,22 @@
 
 package libcore.java.util;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.lang.Iterable;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.Spliterator;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -27,7 +40,7 @@
     public void test_getOrDefault() {
         MapDefaultMethodTester
                 .test_getOrDefault(new LinkedHashMap<>(), true /*acceptsNullKey*/,
-                        true /*acceptsNullValue*/);
+                        true /*acceptsNullValue*/, true /*getAcceptsAnyObject*/);
 
         // Test for access order
         Map<String, String> m = new LinkedHashMap<String, String>(8, .75f, true);
@@ -209,33 +222,46 @@
         assertEquals("value3", newest.getValue());
     }
 
-    // http://b/27929722
-    // This tests the behaviour is consistent with earlier Android releases.
-    // This behaviour is NOT consistent with the RI. Future Android releases
-    // might change this.
+    // This tests the behaviour is consistent with the RI.
+    // This behaviour is NOT consistent with earlier Android releases up to
+    // and including Android N, see http://b/27929722
     public void test_removeEldestEntry() {
         final AtomicBoolean removeEldestEntryReturnValue = new AtomicBoolean(false);
         final AtomicInteger removeEldestEntryCallCount = new AtomicInteger(0);
         LinkedHashMap<String, String> m = new LinkedHashMap<String, String>() {
             @Override
             protected boolean removeEldestEntry(Entry eldest) {
+                int size = size();
+                assertEquals(size, iterableSize(entrySet()));
+                assertEquals(size, iterableSize(keySet()));
+                assertEquals(size, iterableSize(values()));
+                assertEquals(size, removeEldestEntryCallCount.get() + 1);
                 removeEldestEntryCallCount.incrementAndGet();
                 return removeEldestEntryReturnValue.get();
             }
         };
 
-        m.put("foo", "bar");
         assertEquals(0, removeEldestEntryCallCount.get());
-        m.put("baz", "quux");
+        m.put("foo", "bar");
         assertEquals(1, removeEldestEntryCallCount.get());
+        m.put("baz", "quux");
+        assertEquals(2, removeEldestEntryCallCount.get());
 
         removeEldestEntryReturnValue.set(true);
         m.put("foob", "faab");
-        assertEquals(2, removeEldestEntryCallCount.get());
+        assertEquals(3, removeEldestEntryCallCount.get());
         assertEquals(2, m.size());
         assertFalse(m.containsKey("foo"));
     }
 
+    private static<E> int iterableSize(Iterable<E> iterable) {
+        int result = 0;
+        for (E element : iterable) {
+            result++;
+        }
+        return result;
+    }
+
     public void test_replaceAll() {
         LinkedHashMap<String, String> map = new LinkedHashMap<>();
         map.put("one", "1");
@@ -249,12 +275,9 @@
         assertEquals(3, map.size());
 
         try {
-            map.replaceAll(new java.util.function.BiFunction<String, String, String>() {
-                @Override
-                public String apply(String k, String v) {
-                    map.put("foo1", v);
-                    return v;
-                }
+            map.replaceAll((k, v) -> {
+                map.put("foo1", v);
+                return v;
             });
             fail();
         } catch(ConcurrentModificationException expected) {}
@@ -264,4 +287,162 @@
             fail();
         } catch(NullPointerException expected) {}
     }
+
+    public void test_eldest_empty() {
+        LinkedHashMap<String, String> emptyMap = createMap();
+        assertNull(eldest(emptyMap));
+    }
+
+    public void test_eldest_nonempty() {
+        assertEntry("key", "value", eldest(createMap("key", "value")));
+        assertEntry("A", "1", eldest(createMap("A", "1", "B", "2", "C", "3")));
+        assertEntry("A", "4", eldest(createMap("A", "1", "B", "2", "C", "3", "A", "4")));
+        assertEntry("A", "4", eldest(createMap("A", "1", "B", "2", "C", "3", "A", "4", "D", "5")));
+    }
+
+    public void test_eldest_compatibleWithIterationOrder() {
+        check_eldest_comparibleWithIterationOrder(createMap());
+        check_eldest_comparibleWithIterationOrder(createMap("key", "value"));
+        check_eldest_comparibleWithIterationOrder(createMap("A", "1", "B", "2"));
+        check_eldest_comparibleWithIterationOrder(createMap("A", "1", "B", "2", "A", "3"));
+        check_eldest_comparibleWithIterationOrder(createMap("A", "1", "A", "2", "A", "3"));
+
+        Random random = new Random(31337); // arbitrary
+        LinkedHashMap<String, String> m = new LinkedHashMap<>();
+        for (int i = 0; i < 8000; i++) {
+            m.put(String.valueOf(random.nextInt(4000)), String.valueOf(random.nextDouble()));
+        }
+        check_eldest_comparibleWithIterationOrder(m);
+    }
+
+    private void check_eldest_comparibleWithIterationOrder(LinkedHashMap<?, ?> map) {
+        Iterator<? extends Map.Entry<?, ?>> it = map.entrySet().iterator();
+        if (it.hasNext()) {
+            Map.Entry<?, ?> expected = it.next();
+            Object expectedKey = expected.getKey();
+            Object expectedValue = expected.getValue();
+            assertEntry(expectedKey, expectedValue, eldest(map));
+        } else {
+            assertNull(eldest(map));
+        }
+    }
+
+    /**
+     * Check that {@code LinkedHashMap.Entry} compiles and refers to
+     * {@link java.util.Map.Entry}, which is required for source
+     * compatibility with earlier versions of Android.
+     * If this test fails to compile, the code under test is broken.
+     */
+    public void test_entryCompatibility_compiletime() {
+        Class c = LinkedHashMap.Entry.class;
+        assertEquals("java.util.Map$Entry", c.getName());
+        assertEquals(Map.Entry.class, c);
+    }
+
+    /**
+     * Checks that there is no nested class named 'Entry' in LinkedHashMap.
+     * If {@link #test_entryCompatibility_compiletime()} passes but
+     * this test fails, then the test was probably compiled against a
+     * version of LinkedHashMap that does not have a nested Entry class,
+     * but run against a version that does.
+     */
+    public void test_entryCompatibility_runtime() {
+        String forbiddenClassName = "java.util.LinkedHashMap$Entry";
+        try {
+            Class.forName(forbiddenClassName);
+            fail("Class " + forbiddenClassName + " should not exist");
+        } catch (ClassNotFoundException expected) {
+            // pass
+        }
+    }
+
+    public void test_spliterator_keySet() {
+        Map<String, Integer> m = new LinkedHashMap<>();
+        m.put("a", 1);
+        m.put("b", 2);
+        m.put("c", 3);
+        m.put("d", 4);
+        m.put("e", 5);
+        m.put("f", 6);
+        m.put("g", 7);
+        m.put("h", 8);
+        m.put("i", 9);
+        m.put("j", 10);
+        ArrayList<String> expectedKeys = new ArrayList<>(
+                Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"));
+        Set<String> keys = m.keySet();
+        SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys);
+        SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+        SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+        SpliteratorTester.runOrderedTests(keys);
+        SpliteratorTester.runSizedTests(keys.spliterator(), 10);
+        SpliteratorTester.runSubSizedTests(keys.spliterator(), 10);
+        assertEquals(
+                Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED
+                        | Spliterator.SUBSIZED,
+                keys.spliterator().characteristics());
+    }
+
+    public void test_spliterator_values() {
+        Map<String, Integer> m = new LinkedHashMap<>();
+        m.put("a", 1);
+        m.put("b", 2);
+        m.put("c", 3);
+        m.put("d", 4);
+        m.put("e", 5);
+        m.put("f", 6);
+        m.put("g", 7);
+        m.put("h", 8);
+        m.put("i", 9);
+        m.put("j", 10);
+        ArrayList<Integer> expectedValues = new ArrayList<>(
+                Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+        );
+        Collection<Integer> values = m.values();
+        SpliteratorTester.runBasicIterationTests(
+                values.spliterator(), expectedValues);
+        SpliteratorTester.runBasicSplitTests(values, expectedValues);
+        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+        SpliteratorTester.runOrderedTests(values);
+        SpliteratorTester.runSizedTests(values, 10);
+        SpliteratorTester.runSubSizedTests(values, 10);
+        assertEquals(Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED,
+                values.spliterator().characteristics());
+    }
+
+    public void test_spliterator_entrySet() {
+        MapDefaultMethodTester
+                .test_entrySet_spliterator_unordered(new LinkedHashMap<>());
+
+        Map<String, Integer> m = new LinkedHashMap<>(Collections.singletonMap("key", 23));
+        assertEquals(
+                Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED |
+                        Spliterator.SUBSIZED,
+                m.entrySet().spliterator().characteristics());
+    }
+
+    private static Map.Entry<?, ?> eldest(LinkedHashMap<?,?> map) {
+        // Should be the same as: return (map.isEmpty()) ? null : map.entrySet().iterator().next();
+        return map.eldest();
+    }
+
+    private static void assertEntry(Object key, Object value, Map.Entry<?, ?> entry) {
+        String msg = String.format(Locale.US, "Expected (%s, %s), got (%s, %s)",
+                key, value, entry.getKey(), entry.getValue());
+        boolean equal = Objects.equals(key, entry.getKey())
+                && Objects.equals(value, entry.getValue());
+        if (!equal) {
+            fail(msg);
+        }
+    }
+
+    private static<T> LinkedHashMap<T, T> createMap(T... keysAndValues) {
+        assertEquals(0, keysAndValues.length % 2);
+        LinkedHashMap<T, T> result = new LinkedHashMap<>();
+        for (int i = 0; i < keysAndValues.length; i += 2) {
+            result.put(keysAndValues[i], keysAndValues[i+1]);
+        }
+        return result;
+    }
+
 }
diff --git a/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java b/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java
index e3c4ad1..098801b 100644
--- a/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java
+++ b/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java
@@ -16,39 +16,119 @@
 
 package libcore.java.util;
 
+import java.util.AbstractList;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.Spliterator;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertSame;
 
 public class MapDefaultMethodTester {
 
     private MapDefaultMethodTester() {}
 
-    public static void test_getOrDefault(Map<Integer, Double> m, boolean acceptsNullKey,
-            boolean acceptsNullValue) {
-        // Unmapped key
-        assertEquals(-1.0, m.getOrDefault(1, -1.0));
-
-        // Mapped key
-        m.put(1, 11.0);
-        assertEquals(11.0, m.getOrDefault(1, -1.0));
-
-        // Check for null value
-        if (acceptsNullValue) {
-            m.put(1, null);
-            assertEquals(null, m.getOrDefault(1, -1.0));
-        }
-
-        // Check for null key
+    /**
+     * @param getAcceptsAnyObject whether get() and getOrDefault() allow any
+     *        nonnull key Object, returning false rather than throwing
+     *        ClassCastException if the key is inappropriate for the map.
+     */
+    public static void test_getOrDefault(Map<String, String> m, boolean acceptsNullKey,
+            boolean acceptsNullValue, boolean getAcceptsAnyObject) {
+        // absent key
         if (acceptsNullKey) {
-            m.put(null, 1.0);
-            assertEquals(1.0, m.getOrDefault(null, -1.0));
+            checkGetOrDefault("default", m, null, "default");
+            if (acceptsNullValue) {
+                checkGetOrDefault(null, m, null, null);
+            }
         }
+        m.put("key", "value");
+        if (acceptsNullValue) {
+            checkGetOrDefault(null, m, "absentkey", null);
+            if (acceptsNullKey) {
+                checkGetOrDefault(null, m, null, null);
+            }
+        }
+        checkGetOrDefault("default", m, "absentkey", "default");
+        m.put("anotherkey", "anothervalue");
+        checkGetOrDefault("default", m, "absentkey", "default");
+
+        // absent key - inappropriate type
+        boolean getAcceptedObject;
+        try {
+            assertSame("default", m.getOrDefault(new Object(), "default"));
+            getAcceptedObject = true;
+        } catch (ClassCastException e) {
+            getAcceptedObject = false;
+        }
+        assertEquals(getAcceptsAnyObject, getAcceptedObject);
+
+        // present key
+        checkGetOrDefault("value", m, "key", "default");
+        checkGetOrDefault("value", m, "key", new String("value"));
+
+        // null value
+        if (acceptsNullValue) {
+            m.put("keyWithNullValue", null);
+            checkGetOrDefault(null, m, "keyWithNullValue", "default");
+        }
+
+        // null key
+        if (acceptsNullKey) {
+            m.put(null, "valueForNullKey");
+            checkGetOrDefault("valueForNullKey", m, null, "valueForNullKey");
+        }
+    }
+
+    /**
+     * Checks that the value returned by {@link LinkedHashMap#getOrDefault(Object, Object)}
+     * is consistent with various other ways getOrDefault() could be computed.
+     */
+    private static<K, V> void checkGetOrDefault(
+            V expected, Map<K, V> map, K key, V defaultValue) {
+        V actual = map.getOrDefault(key, defaultValue);
+        assertSame(expected, actual);
+
+        assertSame(expected, getOrDefault_hashMap(map, key, defaultValue));
+        assertSame(expected, getOrDefault_optimizeForPresent(map, key, defaultValue));
+        assertSame(expected, getOrDefault_optimizeForAbsent(map, key, defaultValue));
+    }
+
+    /** Implementation of getOrDefault() on top of HashMap.getOrDefault(). */
+    private static<K, V> V getOrDefault_hashMap(Map<K, V> map, K key, V defaultValue) {
+        return new HashMap<>(map).getOrDefault(key, defaultValue);
+    }
+
+    /**
+     * Implementation of Map.getOrDefault() that only needs one lookup if the key is
+     * absent.
+     */
+    private static<K, V> V getOrDefault_optimizeForAbsent(Map<K, V> map, K key, V defaultValue) {
+        return map.containsKey(key) ? map.get(key) : defaultValue;
+    }
+
+    /**
+     *  Implementation of  getOrDefault() that only needs one lookup if the key is
+     *  present and not mapped to null.
+     */
+    private static<K, V> V getOrDefault_optimizeForPresent(Map<K, V> map, K key, V defaultValue) {
+        V result = map.get(key);
+        if (result == null && !map.containsKey(key)) {
+            result = defaultValue;
+        }
+        return result;
     }
 
     public static void test_forEach(Map<Integer, Double> m) {
@@ -63,6 +143,7 @@
         // Null pointer exception for empty function
         try {
             m.forEach(null);
+            fail();
         } catch (NullPointerException expected) {
         }
     }
@@ -275,6 +356,7 @@
         // If the remapping function is null
         try {
             m.computeIfPresent(1, null);
+            fail();
         } catch (NullPointerException expected) {}
 
         if (acceptsNullKey) {
@@ -283,6 +365,7 @@
         } else {
             try {
                 m.computeIfPresent(null, (k, v) -> 5.0);
+                fail();
             } catch (NullPointerException expected) {}
         }
     }
@@ -351,4 +434,79 @@
             } catch (NullPointerException expected) {}
         }
     }
+
+    public static void test_entrySet_spliterator_unordered(Map<String, String> m) {
+        checkEntrySpliterator(m);
+        m.put("key", "value");
+        checkEntrySpliterator(m, "key", "value");
+        m.put("key2", "value2");
+        checkEntrySpliterator(m, "key", "value", "key2", "value2");
+        m.put("key", "newValue");
+        checkEntrySpliterator(m, "key", "newValue", "key2", "value2");
+        m.remove("key2");
+        checkEntrySpliterator(m, "key", "newValue");
+        m.clear();
+
+        // Check 100 entries in random order
+        Random random = new Random(1000); // arbitrary
+
+        final List<Integer> order = new ArrayList<>(new AbstractList<Integer>() {
+            @Override public Integer get(int index) { return index; }
+            @Override public int size() { return 100; }
+        });
+        List<Map.Entry<String, String>> entries = new AbstractList<Map.Entry<String, String>>() {
+            @Override
+            public Map.Entry<String, String> get(int index) {
+                int i = order.get(index);
+                return new AbstractMap.SimpleEntry<>("key" + i, "value" + i);
+            }
+            @Override public int size() { return order.size(); }
+        };
+        Collections.shuffle(order, random); // Pick a random put() order of the entries
+        for (Map.Entry<String, String> entry : entries) {
+            m.put(entry.getKey(), entry.getValue());
+        }
+        Collections.shuffle(order, random); // Pick a different random order for the assertion
+        checkEntrySpliterator(m, new ArrayList<>(entries));
+    }
+
+    private static void checkEntrySpliterator(Map<String, String> m,
+            String... expectedKeysAndValues) {
+        checkEntrySpliterator(m, makeEntries(expectedKeysAndValues));
+    }
+
+    private static void checkEntrySpliterator(Map<String, String> m,
+            ArrayList<Map.Entry<String, String>> expectedEntries) {
+        Set<Map.Entry<String, String>> entrySet = m.entrySet();
+        Comparator<Map.Entry<String, String>> keysThenValuesComparator =
+                Map.Entry.<String, String>comparingByKey()
+                        .thenComparing(Map.Entry.comparingByValue());
+
+        assertTrue(entrySet.spliterator().hasCharacteristics(Spliterator.DISTINCT));
+
+        SpliteratorTester.runBasicIterationTests_unordered(entrySet.spliterator(),
+                expectedEntries, keysThenValuesComparator);
+        SpliteratorTester.runBasicSplitTests(entrySet.spliterator(),
+                expectedEntries, keysThenValuesComparator);
+        SpliteratorTester.testSpliteratorNPE(entrySet.spliterator());
+
+        boolean isSized = entrySet.spliterator().hasCharacteristics(Spliterator.SIZED);
+        if (isSized) {
+            SpliteratorTester.runSizedTests(entrySet.spliterator(), entrySet.size());
+        }
+        Spliterator<?> subSpliterator = entrySet.spliterator().trySplit();
+        if (subSpliterator != null && subSpliterator.hasCharacteristics(Spliterator.SIZED)) {
+            SpliteratorTester.runSubSizedTests(entrySet.spliterator(), entrySet.size());
+        }
+    }
+
+    private static<T> ArrayList<Map.Entry<T, T>> makeEntries(T... keysAndValues) {
+        assertEquals(0, keysAndValues.length % 2);
+        ArrayList<Map.Entry<T, T>> result = new ArrayList<>();
+        for (int i = 0; i < keysAndValues.length; i += 2) {
+            result.add(new AbstractMap.SimpleEntry<>(keysAndValues[i], keysAndValues[i+1]));
+        }
+        return result;
+    }
+
 }
diff --git a/luni/src/test/java/libcore/java/util/SpliteratorTester.java b/luni/src/test/java/libcore/java/util/SpliteratorTester.java
index a5b076b..9538aef 100644
--- a/luni/src/test/java/libcore/java/util/SpliteratorTester.java
+++ b/luni/src/test/java/libcore/java/util/SpliteratorTester.java
@@ -21,6 +21,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Spliterator;
 import java.util.function.Consumer;
 
@@ -55,8 +56,12 @@
         Consumer<T> consumer = (T value) -> recorder.add(value);
 
         // tryAdvance.
-        assertTrue(spliterator.tryAdvance(consumer));
-        assertTrue(expectedElements.contains(recorder.get(0)));
+        if (expectedElements.isEmpty()) {
+            assertFalse(spliterator.tryAdvance(consumer));
+        } else {
+            assertTrue(spliterator.tryAdvance(consumer));
+            assertTrue(expectedElements.contains(recorder.get(0)));
+        }
 
         // forEachRemaining.
         spliterator.forEachRemaining(consumer);
@@ -106,7 +111,8 @@
         ArrayList<T> recorder = new ArrayList<>();
 
         // Advance the original spliterator by one element.
-        assertTrue(spliterator.tryAdvance(value -> recorder.add(value)));
+        boolean didAdvance = spliterator.tryAdvance(value -> recorder.add(value));
+        assertEquals(!expectedElements.isEmpty(), didAdvance);
 
         // Try splitting it.
         Spliterator<T> split1 = spliterator.trySplit();
@@ -168,7 +174,12 @@
         assertEquals(iteration1, iteration2);
     }
 
+    /**
+     * Checks that the specified SIZED Spliterator reports containing the
+     * specified number of elements.
+     */
     public static <T> void runSizedTests(Spliterator<T> spliterator, int expectedSize) {
+        assertTrue(spliterator.hasCharacteristics(Spliterator.SIZED));
         assertEquals(expectedSize, spliterator.estimateSize());
         assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
     }
@@ -177,14 +188,28 @@
         runSizedTests(spliterable.spliterator(), expectedSize);
     }
 
+    /**
+     * Checks that the specified Spliterator and its {@link Spliterator#trySplit()
+     * children} are SIZED and SUBSIZED and report containing the specified number
+     * of elements.
+     */
     public static <T> void runSubSizedTests(Spliterator<T> spliterator, int expectedSize) {
+        assertTrue(spliterator.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
         assertEquals(expectedSize, spliterator.estimateSize());
         assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
 
-
-        Spliterator<T> split1 = spliterator.trySplit();
-        assertEquals(expectedSize, spliterator.estimateSize() + split1.estimateSize());
-        assertEquals(expectedSize, spliterator.getExactSizeIfKnown() + split1.getExactSizeIfKnown());
+        Spliterator<T> child = spliterator.trySplit();
+        assertTrue(spliterator.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
+        if (expectedSize == 0) {
+            assertNull(child);
+            assertEquals(expectedSize, spliterator.estimateSize());
+            assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
+        } else {
+            assertTrue(child.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
+            assertEquals(expectedSize, spliterator.estimateSize() + child.estimateSize());
+            assertEquals(expectedSize,
+                    spliterator.getExactSizeIfKnown() + child.getExactSizeIfKnown());
+        }
     }
 
     public static <T> void runSubSizedTests(Iterable<T> spliterable, int expectedSize) {
diff --git a/luni/src/test/java/libcore/java/util/TreeMapTest.java b/luni/src/test/java/libcore/java/util/TreeMapTest.java
index 355d29d..749ffa1 100644
--- a/luni/src/test/java/libcore/java/util/TreeMapTest.java
+++ b/luni/src/test/java/libcore/java/util/TreeMapTest.java
@@ -495,13 +495,14 @@
                 String::compareTo);
         SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
         SpliteratorTester.testSpliteratorNPE(keys.spliterator());
-
-        assertTrue(keys.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED));
+        assertEquals(
+                Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED,
+                keys.spliterator().characteristics());
         SpliteratorTester.runSortedTests(keys);
         SpliteratorTester.runOrderedTests(keys);
     }
 
-    public void test_spliterator_valueSet() {
+    public void test_spliterator_values() {
         TreeMap<String, String> treeMap = new TreeMap<>();
         treeMap.put("a", "1");
         treeMap.put("b", "2");
@@ -528,7 +529,8 @@
         SpliteratorTester.runBasicSplitTests(values, expectedValues);
         SpliteratorTester.testSpliteratorNPE(values.spliterator());
 
-        assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SIZED));
+        assertEquals(Spliterator.ORDERED | Spliterator.SIZED,
+                values.spliterator().characteristics());
         SpliteratorTester.runSizedTests(values, 16);
         SpliteratorTester.runOrderedTests(values);
     }
@@ -552,20 +554,22 @@
         treeMap.put("o", "15");
         treeMap.put("p", "16");
 
-        Set<Map.Entry<String, String>> values = treeMap.entrySet();
-        ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(values);
+        Set<Map.Entry<String, String>> entries = treeMap.entrySet();
+        ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(entries);
 
         Comparator<Map.Entry<String, String>> comparator =
                 (a, b) -> (a.getKey().compareTo(b.getKey()));
 
-        SpliteratorTester.runBasicIterationTests_unordered(values.spliterator(), expectedValues,
+        SpliteratorTester.runBasicIterationTests_unordered(entries.spliterator(), expectedValues,
                 (a, b) -> (a.getKey().compareTo(b.getKey())));
-        SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator);
-        SpliteratorTester.testSpliteratorNPE(values.spliterator());
+        SpliteratorTester.runBasicSplitTests(entries, expectedValues, comparator);
+        SpliteratorTester.testSpliteratorNPE(entries.spliterator());
 
-        assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED));
-        SpliteratorTester.runSortedTests(values, (a, b) -> (a.getKey().compareTo(b.getKey())));
-        SpliteratorTester.runOrderedTests(values);
+        assertEquals(
+                Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED,
+                entries.spliterator().characteristics());
+        SpliteratorTester.runSortedTests(entries, (a, b) -> (a.getKey().compareTo(b.getKey())));
+        SpliteratorTester.runOrderedTests(entries);
     }
 
     public void test_replaceAll() throws Exception {
@@ -586,12 +590,9 @@
         } catch(NullPointerException expected) {}
 
         try {
-            map.replaceAll(new java.util.function.BiFunction<String, String, String>() {
-                @Override
-                public String apply(String k, String v) {
-                    map.put("foo", v);
-                    return v;
-                }
+            map.replaceAll((k, v) -> {
+                map.put("foo", v);
+                return v;
             });
             fail();
         } catch(ConcurrentModificationException expected) {}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
index 5cbaa8f..54b354e 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
@@ -24,7 +24,8 @@
 
     public void test_getOrDefault() {
         MapDefaultMethodTester.test_getOrDefault(new ConcurrentSkipListMap<>(),
-                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
+                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/,
+                false /*getAcceptsAnyObject*/);
     }
 
     public void test_forEach() {
diff --git a/non_openjdk_java_files.mk b/non_openjdk_java_files.mk
index 0719fdf..14fd77b 100644
--- a/non_openjdk_java_files.mk
+++ b/non_openjdk_java_files.mk
@@ -36,6 +36,7 @@
   dalvik/src/main/java/dalvik/annotation/TestTarget.java \
   dalvik/src/main/java/dalvik/annotation/TestTargetClass.java \
   dalvik/src/main/java/dalvik/annotation/Throws.java \
+  dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java \
   dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java \
   dalvik/src/main/java/dalvik/bytecode/Opcodes.java \
   dalvik/src/main/java/dalvik/system/AllocationLimitError.java \
diff --git a/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java b/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java
deleted file mode 100644
index 68a736c..0000000
--- a/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2012, 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.  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;
-
-import java.lang.invoke.MethodHandles.Lookup;
-import java.lang.reflect.Member;
-
-public
-interface MethodHandleInfo {
-    public static final int REF_getField                = 0;
-    public static final int REF_getStatic               = 0;
-    public static final int REF_putField                = 0;
-    public static final int REF_putStatic               = 0;
-    public static final int REF_invokeVirtual           = 0;
-    public static final int REF_invokeStatic            = 0;
-    public static final int REF_invokeSpecial           = 0;
-    public static final int REF_newInvokeSpecial        = 0;
-    public static final int REF_invokeInterface         = 0;
-
-    public int getReferenceKind();
-
-    public Class<?> getDeclaringClass();
-
-    public String getName();
-
-    public MethodType getMethodType();
-
-    public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
-
-    public int getModifiers();
-
-    public default boolean isVarArgs()  { return false; }
-
-    public static String referenceKindToString(int referenceKind) { return null; }
-
-    public static String toString(int kind, Class<?> defc, String name, MethodType type) {
-        return null;
-    }
-}
diff --git a/ojluni/src/main/java/java/lang/Class.java b/ojluni/src/main/java/java/lang/Class.java
index 66a1917..44eadce 100644
--- a/ojluni/src/main/java/java/lang/Class.java
+++ b/ojluni/src/main/java/java/lang/Class.java
@@ -28,42 +28,24 @@
 
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Array;
-import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Member;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.GenericDeclaration;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.ref.SoftReference;
 import java.io.InputStream;
-import java.io.ObjectStreamField;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
-import java.util.LinkedList;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.Map;
 import java.util.HashMap;
-import sun.misc.Unsafe;
 import sun.reflect.CallerSensitive;
 import java.lang.annotation.Inherited;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Proxy;
-import sun.reflect.annotation.*;
-import sun.reflect.misc.ReflectUtil;
 
 import java.io.Serializable;
-import java.lang.reflect.AccessibleObject;
 import com.android.dex.Dex;
 import dalvik.system.VMStack;
 import libcore.reflect.InternalNames;
@@ -72,7 +54,6 @@
 import libcore.util.BasicLruCache;
 import libcore.util.CollectionUtils;
 import libcore.util.EmptyArray;
-import libcore.util.SneakyThrow;
 import java.util.Collections;
 
 /**
@@ -258,8 +239,6 @@
     /** Offset of the first virtual method defined in this class in the methods array. */
     private transient short virtualMethodsOffset;
 
-    private AnnotationType annotationType;
-
     private static final int ANNOTATION  = 0x00002000;
     private static final int ENUM        = 0x00004000;
     private static final int SYNTHETIC   = 0x00001000;
@@ -1760,7 +1739,6 @@
      * resolution checks first. If no fields exist, the list is not modified.
      *
      * @param publicOnly Whether to return only public fields.
-     * @param fields A list to populate with declared fields.
      * @hide
      */
     public native Field[] getDeclaredFieldsUnchecked(boolean publicOnly);
@@ -1819,7 +1797,6 @@
      * resolution checks first. If no methods exist, the list is not modified.
      *
      * @param publicOnly Whether to return only public methods.
-     * @param methods A list to populate with declared methods.
      * @hide
      */
     public native Method[] getDeclaredMethodsUnchecked(boolean publicOnly);
@@ -1867,8 +1844,6 @@
     /**
      * Returns the constructor with the given parameters if it is defined by this class;
      * {@code null} otherwise. This may return a non-public member.
-     *
-     * @param args the types of the parameters to the constructor.
      */
     private native Constructor<?>[] getDeclaredConstructorsInternal(boolean publicOnly);
 
@@ -2477,18 +2452,6 @@
      */
     private native boolean isDeclaredAnnotationPresent(Class<? extends Annotation> annotationClass);
 
-    // Annotation types cache their internal (AnnotationType) form
-
-    /** @hide */
-    public void setAnnotationType(AnnotationType type) {
-        annotationType = type;
-    }
-
-    /** @hide */
-    public AnnotationType getAnnotationType() {
-        return annotationType;
-    }
-
     private String getSignatureAttribute() {
         String[] annotation = getSignatureAnnotation();
         if (annotation == null) {
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java b/ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java
new file mode 100644
index 0000000..08ce6f0
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2012, 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.  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;
+
+import java.lang.reflect.*;
+import java.util.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+/**
+ * A symbolic reference obtained by cracking a direct method handle
+ * into its consitutent symbolic parts.
+ * To crack a direct method handle, call {@code Lookup#revealDirect Lookup.revealDirect}.
+ * <h1><a name="directmh"></a>Direct Method Handles</h1>
+ * A <em>direct method handle</em> represents a method, constructor, or field without
+ * any intervening argument bindings or other transformations.
+ * The method, constructor, or field referred to by a direct method handle is called
+ * its <em>underlying member</em>.
+ * Direct method handles may be obtained in any of these ways:
+ * <ul>
+ * <li>By executing an {@code ldc} instruction on a {@code CONSTANT_MethodHandle} constant.
+ *     (See the Java Virtual Machine Specification, sections 4.4.8 and 5.4.3.)
+ * <li>By calling one of the <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>,
+ *     such as {@code Lookup#findVirtual Lookup.findVirtual},
+ *     to resolve a symbolic reference into a method handle.
+ *     A symbolic reference consists of a class, name string, and type.
+ * <li>By calling the factory method {@code Lookup#unreflect Lookup.unreflect}
+ *     or {@code Lookup#unreflectSpecial Lookup.unreflectSpecial}
+ *     to convert a {@link Method} into a method handle.
+ * <li>By calling the factory method {@code Lookup#unreflectConstructor Lookup.unreflectConstructor}
+ *     to convert a {@link Constructor} into a method handle.
+ * <li>By calling the factory method {@code Lookup#unreflectGetter Lookup.unreflectGetter}
+ *     or {@code Lookup#unreflectSetter Lookup.unreflectSetter}
+ *     to convert a {@link Field} into a method handle.
+ * </ul>
+ *
+ * <h1>Restrictions on Cracking</h1>
+ * Given a suitable {@code Lookup} object, it is possible to crack any direct method handle
+ * to recover a symbolic reference for the underlying method, constructor, or field.
+ * Cracking must be done via a {@code Lookup} object equivalent to that which created
+ * the target method handle, or which has enough access permissions to recreate
+ * an equivalent method handle.
+ * <p>
+ * If the underlying method is <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a>,
+ * the direct method handle will have been "bound" to a particular caller class, the
+ * {@code java.lang.invoke.MethodHandles.Lookup#lookupClass() lookup class}
+ * of the lookup object used to create it.
+ * Cracking this method handle with a different lookup class will fail
+ * even if the underlying method is public (like {@code Class.forName}).
+ * <p>
+ * The requirement of lookup object matching provides a "fast fail" behavior
+ * for programs which may otherwise trust erroneous revelation of a method
+ * handle with symbolic information (or caller binding) from an unexpected scope.
+ * Use {@code java.lang.invoke.MethodHandles#reflectAs} to override this limitation.
+ *
+ * <h1><a name="refkinds"></a>Reference kinds</h1>
+ * The <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>
+ * correspond to all major use cases for methods, constructors, and fields.
+ * These use cases may be distinguished using small integers as follows:
+ * <table border=1 cellpadding=5 summary="reference kinds">
+ * <tr><th>reference kind</th><th>descriptive name</th><th>scope</th><th>member</th><th>behavior</th></tr>
+ * <tr>
+ *     <td>{@code 1}</td><td>{@code REF_getField}</td><td>{@code class}</td>
+ *     <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 2}</td><td>{@code REF_getStatic}</td><td>{@code class} or {@code interface}</td>
+ *     <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 3}</td><td>{@code REF_putField}</td><td>{@code class}</td>
+ *     <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 4}</td><td>{@code REF_putStatic}</td><td>{@code class}</td>
+ *     <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 5}</td><td>{@code REF_invokeVirtual}</td><td>{@code class}</td>
+ *     <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 6}</td><td>{@code REF_invokeStatic}</td><td>{@code class} or {@code interface}</td>
+ *     <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 7}</td><td>{@code REF_invokeSpecial}</td><td>{@code class} or {@code interface}</td>
+ *     <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 8}</td><td>{@code REF_newInvokeSpecial}</td><td>{@code class}</td>
+ *     <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
+ * </tr>
+ * <tr>
+ *     <td>{@code 9}</td><td>{@code REF_invokeInterface}</td><td>{@code interface}</td>
+ *     <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * </table>
+ * @since 1.8
+ */
+// TODO(narayan) : Change @code back to @link once MethodHandles has been imported.
+public
+interface MethodHandleInfo {
+    /**
+     * A direct method handle reference kind,
+     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
+     */
+    // Android-changed: Inlined the values of these constants from MethodHandleNatives.Constants.
+    public static final int
+        REF_getField                = 1,
+        REF_getStatic               = 2,
+        REF_putField                = 3,
+        REF_putStatic               = 4,
+        REF_invokeVirtual           = 5,
+        REF_invokeStatic            = 6,
+        REF_invokeSpecial           = 7,
+        REF_newInvokeSpecial        = 8,
+        REF_invokeInterface         = 9;
+
+    /**
+     * Returns the reference kind of the cracked method handle, which in turn
+     * determines whether the method handle's underlying member was a constructor, method, or field.
+     * See the <a href="MethodHandleInfo.html#refkinds">table above</a> for definitions.
+     * @return the integer code for the kind of reference used to access the underlying member
+     */
+    public int getReferenceKind();
+
+    /**
+     * Returns the class in which the cracked method handle's underlying member was defined.
+     * @return the declaring class of the underlying member
+     */
+    public Class<?> getDeclaringClass();
+
+    /**
+     * Returns the name of the cracked method handle's underlying member.
+     * This is {@code "&lt;init&gt;"} if the underlying member was a constructor,
+     * else it is a simple method name or field name.
+     * @return the simple name of the underlying member
+     */
+    public String getName();
+
+    /**
+     * Returns the nominal type of the cracked symbolic reference, expressed as a method type.
+     * If the reference is to a constructor, the return type will be {@code void}.
+     * If it is to a non-static method, the method type will not mention the {@code this} parameter.
+     * If it is to a field and the requested access is to read the field,
+     * the method type will have no parameters and return the field type.
+     * If it is to a field and the requested access is to write the field,
+     * the method type will have one parameter of the field type and return {@code void}.
+     * <p>
+     * Note that original direct method handle may include a leading {@code this} parameter,
+     * or (in the case of a constructor) will replace the {@code void} return type
+     * with the constructed class.
+     * The nominal type does not include any {@code this} parameter,
+     * and (in the case of a constructor) will return {@code void}.
+     * @return the type of the underlying member, expressed as a method type
+     */
+    public MethodType getMethodType();
+
+    // Utility methods.
+    // NOTE: class/name/type and reference kind constitute a symbolic reference
+    // member and modifiers are an add-on, derived from Core Reflection (or the equivalent)
+
+    /**
+     * Reflects the underlying member as a method, constructor, or field object.
+     * If the underlying member is public, it is reflected as if by
+     * {@code getMethod}, {@code getConstructor}, or {@code getField}.
+     * Otherwise, it is reflected as if by
+     * {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
+     * The underlying member must be accessible to the given lookup object.
+     * @param <T> the desired type of the result, either {@link Member} or a subtype
+     * @param expected a class object representing the desired result type {@code T}
+     * @param lookup the lookup object that created this MethodHandleInfo, or one with equivalent access privileges
+     * @return a reference to the method, constructor, or field object
+     * @exception ClassCastException if the member is not of the expected type
+     * @exception NullPointerException if either argument is {@code null}
+     * @exception IllegalArgumentException if the underlying member is not accessible to the given lookup object
+     */
+    // TODO(narayan): change the second argument back to MethodHandles.Lookup once that
+    // class has been introduced.
+    //
+    // public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
+    //
+    // We need to temporarily keep the existing version because of a circular dependency between
+    // the two classes.
+    public <T extends Member> T reflectAs(Class<T> expected, Object lookup);
+
+    /**
+     * Returns the access modifiers of the underlying member.
+     * @return the Java language modifiers for underlying member,
+     *         or -1 if the member cannot be accessed
+     * @see Modifier
+     * @see #reflectAs
+     */
+    public int getModifiers();
+
+    /**
+     * Determines if the underlying member was a variable arity method or constructor.
+     * Such members are represented by method handles that are varargs collectors.
+     * @implSpec
+     * This produces a result equivalent to:
+     * <pre>{@code
+     *     getReferenceKind() >= REF_invokeVirtual && Modifier.isTransient(getModifiers())
+     * }</pre>
+     *
+     *
+     * @return {@code true} if and only if the underlying member was declared with variable arity.
+     */
+    // spelling derived from java.lang.reflect.Executable, not MethodHandle.isVarargsCollector
+    public default boolean isVarArgs()  {
+        // fields are never varargs:
+        if (refKindIsField(getReferenceKind()))
+            return false;
+        // not in the public API: Modifier.VARARGS
+        final int ACC_VARARGS = 0x00000080;  // from JVMS 4.6 (Table 4.20)
+
+        // Android-changed: Removed assert() due to http://b/30862573
+        // assert(ACC_VARARGS == Modifier.TRANSIENT);
+        return Modifier.isTransient(getModifiers());
+    }
+
+    /**
+     * Returns the descriptive name of the given reference kind,
+     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
+     * The conventional prefix "REF_" is omitted.
+     * @param referenceKind an integer code for a kind of reference used to access a class member
+     * @return a mixed-case string such as {@code "getField"}
+     * @exception IllegalArgumentException if the argument is not a valid
+     *            <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+     */
+    public static String referenceKindToString(int referenceKind) {
+        if (!refKindIsValid(referenceKind))
+            throw newIllegalArgumentException("invalid reference kind", referenceKind);
+        return refKindName(referenceKind);
+    }
+
+    /**
+     * Returns a string representation for a {@code MethodHandleInfo},
+     * given the four parts of its symbolic reference.
+     * This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
+     * {@linkplain #referenceKindToString reference kind string} for {@code kind},
+     * {@code C} is the {@linkplain java.lang.Class#getName name} of {@code defc}
+     * {@code N} is the {@code name}, and
+     * {@code MT} is the {@code type}.
+     * These four values may be obtained from the
+     * {@linkplain #getReferenceKind reference kind},
+     * {@linkplain #getDeclaringClass declaring class},
+     * {@linkplain #getName member name},
+     * and {@linkplain #getMethodType method type}
+     * of a {@code MethodHandleInfo} object.
+     *
+     * @implSpec
+     * This produces a result equivalent to:
+     * <pre>{@code
+     *     String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type)
+     * }</pre>
+     *
+     * @param kind the {@linkplain #getReferenceKind reference kind} part of the symbolic reference
+     * @param defc the {@linkplain #getDeclaringClass declaring class} part of the symbolic reference
+     * @param name the {@linkplain #getName member name} part of the symbolic reference
+     * @param type the {@linkplain #getMethodType method type} part of the symbolic reference
+     * @return a string of the form {@code "RK C.N:MT"}
+     * @exception IllegalArgumentException if the first argument is not a valid
+     *            <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+     * @exception NullPointerException if any reference argument is {@code null}
+     */
+    public static String toString(int kind, Class<?> defc, String name, MethodType type) {
+        Objects.requireNonNull(name); Objects.requireNonNull(type);
+        return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
+    }
+
+    // Android-changed: Inlined from MethodHandleNatives and changed to avoid references to
+    // REF_NONE and REF_LIMITED
+    static boolean refKindIsValid(int refKind) {
+        return (refKind >= REF_getField && refKind <= REF_invokeInterface);
+    }
+
+    // Android-changed: Inlined from MethodHandleNatives.
+    static boolean refKindIsField(int refKind) {
+        return (refKind <= REF_putStatic);
+    }
+
+    // Android-changed: Inlined from MethodHandleNatives and replaced 'byte' argument with
+    // 'int'.
+    static String refKindName(int refKind) {
+        switch (refKind) {
+            case REF_getField:          return "getField";
+            case REF_getStatic:         return "getStatic";
+            case REF_putField:          return "putField";
+            case REF_putStatic:         return "putStatic";
+            case REF_invokeVirtual:     return "invokeVirtual";
+            case REF_invokeStatic:      return "invokeStatic";
+            case REF_invokeSpecial:     return "invokeSpecial";
+            case REF_newInvokeSpecial:  return "newInvokeSpecial";
+            case REF_invokeInterface:   return "invokeInterface";
+            default:                    return "REF_???";
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/nio/ByteBuffer.java b/ojluni/src/main/java/java/nio/ByteBuffer.java
index 523bbda..c78447f 100644
--- a/ojluni/src/main/java/java/nio/ByteBuffer.java
+++ b/ojluni/src/main/java/java/nio/ByteBuffer.java
@@ -533,12 +533,12 @@
         if (!isAccessible()) {
             throw new IllegalStateException("buffer is inaccessible");
         }
-        if (isReadOnly) {
-            throw new ReadOnlyBufferException();
-        }
         if (src == this) {
             throw new IllegalArgumentException();
         }
+        if (isReadOnly) {
+            throw new ReadOnlyBufferException();
+        }
         int n = src.remaining();
         if (n > remaining()) {
             throw new BufferOverflowException();
diff --git a/ojluni/src/main/java/java/nio/HeapCharBuffer.java b/ojluni/src/main/java/java/nio/HeapCharBuffer.java
index f67c91f..fad4fa6 100644
--- a/ojluni/src/main/java/java/nio/HeapCharBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapCharBuffer.java
@@ -165,12 +165,13 @@
     }
 
     public CharBuffer put(CharBuffer src) {
+        if (src == this) {
+            throw new IllegalArgumentException();
+        }
         if (isReadOnly) {
             throw new ReadOnlyBufferException();
         }
         if (src instanceof HeapCharBuffer) {
-            if (src == this)
-                throw new IllegalArgumentException();
             HeapCharBuffer sb = (HeapCharBuffer) src;
             int n = sb.remaining();
             if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java b/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java
index e3d9634..02634dd 100644
--- a/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java
@@ -160,12 +160,13 @@
     }
 
     public DoubleBuffer put(DoubleBuffer src) {
+        if (src == this) {
+            throw new IllegalArgumentException();
+        }
         if (isReadOnly) {
             throw new ReadOnlyBufferException();
         }
         if (src instanceof HeapDoubleBuffer) {
-            if (src == this)
-                throw new IllegalArgumentException();
             HeapDoubleBuffer sb = (HeapDoubleBuffer)src;
             int n = sb.remaining();
             if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapFloatBuffer.java b/ojluni/src/main/java/java/nio/HeapFloatBuffer.java
index ca341a0..42dd8ce 100644
--- a/ojluni/src/main/java/java/nio/HeapFloatBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapFloatBuffer.java
@@ -159,12 +159,13 @@
     }
 
     public FloatBuffer put(FloatBuffer src) {
+        if (src == this) {
+            throw new IllegalArgumentException();
+        }
         if (isReadOnly) {
             throw new ReadOnlyBufferException();
         }
         if (src instanceof HeapFloatBuffer) {
-            if (src == this)
-                throw new IllegalArgumentException();
             HeapFloatBuffer sb = (HeapFloatBuffer) src;
             int n = sb.remaining();
             if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapIntBuffer.java b/ojluni/src/main/java/java/nio/HeapIntBuffer.java
index 642e782..b4f3bf7 100644
--- a/ojluni/src/main/java/java/nio/HeapIntBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapIntBuffer.java
@@ -160,12 +160,13 @@
     }
 
     public IntBuffer put(IntBuffer src) {
+        if (src == this) {
+            throw new IllegalArgumentException();
+        }
         if (isReadOnly) {
             throw new ReadOnlyBufferException();
         }
         if (src instanceof HeapIntBuffer) {
-            if (src == this)
-                throw new IllegalArgumentException();
             HeapIntBuffer sb = (HeapIntBuffer) src;
             int n = sb.remaining();
             if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapLongBuffer.java b/ojluni/src/main/java/java/nio/HeapLongBuffer.java
index 60596b0..c0678b3 100644
--- a/ojluni/src/main/java/java/nio/HeapLongBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapLongBuffer.java
@@ -161,12 +161,13 @@
     }
 
     public LongBuffer put(LongBuffer src) {
+        if (src == this) {
+            throw new IllegalArgumentException();
+        }
         if (isReadOnly) {
             throw new ReadOnlyBufferException();
         }
         if (src instanceof HeapLongBuffer) {
-            if (src == this)
-                throw new IllegalArgumentException();
             HeapLongBuffer sb = (HeapLongBuffer) src;
             int n = sb.remaining();
             if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapShortBuffer.java b/ojluni/src/main/java/java/nio/HeapShortBuffer.java
index f78f241..af39261 100644
--- a/ojluni/src/main/java/java/nio/HeapShortBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapShortBuffer.java
@@ -160,12 +160,13 @@
     }
 
     public ShortBuffer put(ShortBuffer src) {
+        if (src == this) {
+            throw new IllegalArgumentException();
+        }
         if (isReadOnly) {
             throw new ReadOnlyBufferException();
         }
         if (src instanceof HeapShortBuffer) {
-            if (src == this)
-                throw new IllegalArgumentException();
             HeapShortBuffer sb = (HeapShortBuffer)src;
             int n = sb.remaining();
             if (n > remaining())
diff --git a/ojluni/src/main/java/java/util/HashMap.java b/ojluni/src/main/java/java/util/HashMap.java
index f47d681..f2cedf8 100644
--- a/ojluni/src/main/java/java/util/HashMap.java
+++ b/ojluni/src/main/java/java/util/HashMap.java
@@ -1,5 +1,4 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -26,10 +25,15 @@
 
 package java.util;
 
-import java.io.*;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.Serializable;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
-import java.util.function.BiConsumer;
+import java.util.function.Function;
 
 /**
  * Hash table based implementation of the <tt>Map</tt> interface.  This
@@ -60,20 +64,25 @@
  * structures are rebuilt) so that the hash table has approximately twice the
  * number of buckets.
  *
- * <p>As a general rule, the default load factor (.75) offers a good tradeoff
- * between time and space costs.  Higher values decrease the space overhead
- * but increase the lookup cost (reflected in most of the operations of the
- * <tt>HashMap</tt> class, including <tt>get</tt> and <tt>put</tt>).  The
- * expected number of entries in the map and its load factor should be taken
- * into account when setting its initial capacity, so as to minimize the
- * number of rehash operations.  If the initial capacity is greater
- * than the maximum number of entries divided by the load factor, no
- * rehash operations will ever occur.
+ * <p>As a general rule, the default load factor (.75) offers a good
+ * tradeoff between time and space costs.  Higher values decrease the
+ * space overhead but increase the lookup cost (reflected in most of
+ * the operations of the <tt>HashMap</tt> class, including
+ * <tt>get</tt> and <tt>put</tt>).  The expected number of entries in
+ * the map and its load factor should be taken into account when
+ * setting its initial capacity, so as to minimize the number of
+ * rehash operations.  If the initial capacity is greater than the
+ * maximum number of entries divided by the load factor, no rehash
+ * operations will ever occur.
  *
- * <p>If many mappings are to be stored in a <tt>HashMap</tt> instance,
- * creating it with a sufficiently large capacity will allow the mappings to
- * be stored more efficiently than letting it perform automatic rehashing as
- * needed to grow the table.
+ * <p>If many mappings are to be stored in a <tt>HashMap</tt>
+ * instance, creating it with a sufficiently large capacity will allow
+ * the mappings to be stored more efficiently than letting it perform
+ * automatic rehashing as needed to grow the table.  Note that using
+ * many keys with the same {@code hashCode()} is a sure way to slow
+ * down performance of any hash table. To ameliorate impact, when keys
+ * are {@link Comparable}, this class may use comparison order among
+ * keys to help break ties.
  *
  * <p><strong>Note that this implementation is not synchronized.</strong>
  * If multiple threads access a hash map concurrently, and at least one of
@@ -125,16 +134,105 @@
  * @see     Hashtable
  * @since   1.2
  */
+public class HashMap<K,V> extends AbstractMap<K,V>
+    implements Map<K,V>, Cloneable, Serializable {
 
-public class HashMap<K,V>
-    extends AbstractMap<K,V>
-    implements Map<K,V>, Cloneable, Serializable
-{
+    private static final long serialVersionUID = 362498820763181265L;
+
+    /*
+     * Implementation notes.
+     *
+     * This map usually acts as a binned (bucketed) hash table, but
+     * when bins get too large, they are transformed into bins of
+     * TreeNodes, each structured similarly to those in
+     * java.util.TreeMap. Most methods try to use normal bins, but
+     * relay to TreeNode methods when applicable (simply by checking
+     * instanceof a node).  Bins of TreeNodes may be traversed and
+     * used like any others, but additionally support faster lookup
+     * when overpopulated. However, since the vast majority of bins in
+     * normal use are not overpopulated, checking for existence of
+     * tree bins may be delayed in the course of table methods.
+     *
+     * Tree bins (i.e., bins whose elements are all TreeNodes) are
+     * ordered primarily by hashCode, but in the case of ties, if two
+     * elements are of the same "class C implements Comparable<C>",
+     * type then their compareTo method is used for ordering. (We
+     * conservatively check generic types via reflection to validate
+     * this -- see method comparableClassFor).  The added complexity
+     * of tree bins is worthwhile in providing worst-case O(log n)
+     * operations when keys either have distinct hashes or are
+     * orderable, Thus, performance degrades gracefully under
+     * accidental or malicious usages in which hashCode() methods
+     * return values that are poorly distributed, as well as those in
+     * which many keys share a hashCode, so long as they are also
+     * Comparable. (If neither of these apply, we may waste about a
+     * factor of two in time and space compared to taking no
+     * precautions. But the only known cases stem from poor user
+     * programming practices that are already so slow that this makes
+     * little difference.)
+     *
+     * Because TreeNodes are about twice the size of regular nodes, we
+     * use them only when bins contain enough nodes to warrant use
+     * (see TREEIFY_THRESHOLD). And when they become too small (due to
+     * removal or resizing) they are converted back to plain bins.  In
+     * usages with well-distributed user hashCodes, tree bins are
+     * rarely used.  Ideally, under random hashCodes, the frequency of
+     * nodes in bins follows a Poisson distribution
+     * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+     * parameter of about 0.5 on average for the default resizing
+     * threshold of 0.75, although with a large variance because of
+     * resizing granularity. Ignoring variance, the expected
+     * occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
+     * factorial(k)). The first values are:
+     *
+     * 0:    0.60653066
+     * 1:    0.30326533
+     * 2:    0.07581633
+     * 3:    0.01263606
+     * 4:    0.00157952
+     * 5:    0.00015795
+     * 6:    0.00001316
+     * 7:    0.00000094
+     * 8:    0.00000006
+     * more: less than 1 in ten million
+     *
+     * The root of a tree bin is normally its first node.  However,
+     * sometimes (currently only upon Iterator.remove), the root might
+     * be elsewhere, but can be recovered following parent links
+     * (method TreeNode.root()).
+     *
+     * All applicable internal methods accept a hash code as an
+     * argument (as normally supplied from a public method), allowing
+     * them to call each other without recomputing user hashCodes.
+     * Most internal methods also accept a "tab" argument, that is
+     * normally the current table, but may be a new or old one when
+     * resizing or converting.
+     *
+     * When bin lists are treeified, split, or untreeified, we keep
+     * them in the same relative access/traversal order (i.e., field
+     * Node.next) to better preserve locality, and to slightly
+     * simplify handling of splits and traversals that invoke
+     * iterator.remove. When using comparators on insertion, to keep a
+     * total ordering (or as close as is required here) across
+     * rebalancings, we compare classes and identityHashCodes as
+     * tie-breakers.
+     *
+     * The use and transitions among plain vs tree modes is
+     * complicated by the existence of subclass LinkedHashMap. See
+     * below for hook methods defined to be invoked upon insertion,
+     * removal and access that allow LinkedHashMap internals to
+     * otherwise remain independent of these mechanics. (This also
+     * requires that a map instance be passed to some utility methods
+     * that may create new nodes.)
+     *
+     * The concurrent-programming-like SSA-based coding style helps
+     * avoid aliasing errors amid all of the twisty pointer operations.
+     */
 
     /**
      * The default initial capacity - MUST be a power of two.
      */
-    static final int DEFAULT_INITIAL_CAPACITY = 4;
+    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
 
     /**
      * The maximum capacity, used if a higher value is implicitly specified
@@ -149,14 +247,158 @@
     static final float DEFAULT_LOAD_FACTOR = 0.75f;
 
     /**
-     * An empty table instance to share when the table is not inflated.
+     * The bin count threshold for using a tree rather than list for a
+     * bin.  Bins are converted to trees when adding an element to a
+     * bin with at least this many nodes. The value must be greater
+     * than 2 and should be at least 8 to mesh with assumptions in
+     * tree removal about conversion back to plain bins upon
+     * shrinkage.
      */
-    static final HashMapEntry<?,?>[] EMPTY_TABLE = {};
+    static final int TREEIFY_THRESHOLD = 8;
 
     /**
-     * The table, resized as necessary. Length MUST Always be a power of two.
+     * The bin count threshold for untreeifying a (split) bin during a
+     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+     * most 6 to mesh with shrinkage detection under removal.
      */
-    transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
+    static final int UNTREEIFY_THRESHOLD = 6;
+
+    /**
+     * The smallest table capacity for which bins may be treeified.
+     * (Otherwise the table is resized if too many nodes in a bin.)
+     * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
+     * between resizing and treeification thresholds.
+     */
+    static final int MIN_TREEIFY_CAPACITY = 64;
+
+    /**
+     * Basic hash bin node, used for most entries.  (See below for
+     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
+     */
+    static class Node<K,V> implements Map.Entry<K,V> {
+        final int hash;
+        final K key;
+        V value;
+        Node<K,V> next;
+
+        Node(int hash, K key, V value, Node<K,V> next) {
+            this.hash = hash;
+            this.key = key;
+            this.value = value;
+            this.next = next;
+        }
+
+        public final K getKey()        { return key; }
+        public final V getValue()      { return value; }
+        public final String toString() { return key + "=" + value; }
+
+        public final int hashCode() {
+            return Objects.hashCode(key) ^ Objects.hashCode(value);
+        }
+
+        public final V setValue(V newValue) {
+            V oldValue = value;
+            value = newValue;
+            return oldValue;
+        }
+
+        public final boolean equals(Object o) {
+            if (o == this)
+                return true;
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+                if (Objects.equals(key, e.getKey()) &&
+                    Objects.equals(value, e.getValue()))
+                    return true;
+            }
+            return false;
+        }
+    }
+
+    /* ---------------- Static utilities -------------- */
+
+    /**
+     * Computes key.hashCode() and spreads (XORs) higher bits of hash
+     * to lower.  Because the table uses power-of-two masking, sets of
+     * hashes that vary only in bits above the current mask will
+     * always collide. (Among known examples are sets of Float keys
+     * holding consecutive whole numbers in small tables.)  So we
+     * apply a transform that spreads the impact of higher bits
+     * downward. There is a tradeoff between speed, utility, and
+     * quality of bit-spreading. Because many common sets of hashes
+     * are already reasonably distributed (so don't benefit from
+     * spreading), and because we use trees to handle large sets of
+     * collisions in bins, we just XOR some shifted bits in the
+     * cheapest possible way to reduce systematic lossage, as well as
+     * to incorporate impact of the highest bits that would otherwise
+     * never be used in index calculations because of table bounds.
+     */
+    static final int hash(Object key) {
+        int h;
+        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+    }
+
+    /**
+     * Returns x's Class if it is of the form "class C implements
+     * Comparable<C>", else null.
+     */
+    static Class<?> comparableClassFor(Object x) {
+        if (x instanceof Comparable) {
+            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
+            if ((c = x.getClass()) == String.class) // bypass checks
+                return c;
+            if ((ts = c.getGenericInterfaces()) != null) {
+                for (int i = 0; i < ts.length; ++i) {
+                    if (((t = ts[i]) instanceof ParameterizedType) &&
+                        ((p = (ParameterizedType)t).getRawType() ==
+                         Comparable.class) &&
+                        (as = p.getActualTypeArguments()) != null &&
+                        as.length == 1 && as[0] == c) // type arg is c
+                        return c;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns k.compareTo(x) if x matches kc (k's screened comparable
+     * class), else 0.
+     */
+    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
+    static int compareComparables(Class<?> kc, Object k, Object x) {
+        return (x == null || x.getClass() != kc ? 0 :
+                ((Comparable)k).compareTo(x));
+    }
+
+    /**
+     * Returns a power of two size for the given target capacity.
+     */
+    static final int tableSizeFor(int cap) {
+        int n = cap - 1;
+        n |= n >>> 1;
+        n |= n >>> 2;
+        n |= n >>> 4;
+        n |= n >>> 8;
+        n |= n >>> 16;
+        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+    }
+
+    /* ---------------- Fields -------------- */
+
+    /**
+     * The table, initialized on first use, and resized as
+     * necessary. When allocated, length is always a power of two.
+     * (We also tolerate length zero in some operations to allow
+     * bootstrapping mechanics that are currently not needed.)
+     */
+    transient Node<K,V>[] table;
+
+    /**
+     * Holds cached entrySet(). Note that AbstractMap fields are used
+     * for keySet() and values().
+     */
+    transient Set<Map.Entry<K,V>> entrySet;
 
     /**
      * The number of key-value mappings contained in this map.
@@ -164,23 +406,6 @@
     transient int size;
 
     /**
-     * The next size value at which to resize (capacity * load factor).
-     * @serial
-     */
-    // If table == EMPTY_TABLE then this is the initial capacity at which the
-    // table will be created when inflated.
-    int threshold;
-
-    /**
-     * The load factor for the hash table.
-     *
-     * @serial
-     */
-    // Android-Note: We always use a load factor of 0.75 and ignore any explicitly
-    // selected values.
-    final float loadFactor = DEFAULT_LOAD_FACTOR;
-
-    /**
      * The number of times this HashMap has been structurally modified
      * Structural modifications are those that change the number of mappings in
      * the HashMap or otherwise modify its internal structure (e.g.,
@@ -190,6 +415,26 @@
     transient int modCount;
 
     /**
+     * The next size value at which to resize (capacity * load factor).
+     *
+     * @serial
+     */
+    // (The javadoc description is true upon serialization.
+    // Additionally, if the table array has not been allocated, this
+    // field holds the initial array capacity, or zero signifying
+    // DEFAULT_INITIAL_CAPACITY.)
+    int threshold;
+
+    /**
+     * The load factor for the hash table.
+     *
+     * @serial
+     */
+    final float loadFactor;
+
+    /* ---------------- Public operations -------------- */
+
+    /**
      * Constructs an empty <tt>HashMap</tt> with the specified initial
      * capacity and load factor.
      *
@@ -202,23 +447,13 @@
         if (initialCapacity < 0)
             throw new IllegalArgumentException("Illegal initial capacity: " +
                                                initialCapacity);
-        if (initialCapacity > MAXIMUM_CAPACITY) {
+        if (initialCapacity > MAXIMUM_CAPACITY)
             initialCapacity = MAXIMUM_CAPACITY;
-        } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
-            initialCapacity = DEFAULT_INITIAL_CAPACITY;
-        }
-
         if (loadFactor <= 0 || Float.isNaN(loadFactor))
             throw new IllegalArgumentException("Illegal load factor: " +
                                                loadFactor);
-        // Android-Note: We always use the default load factor of 0.75f.
-
-        // This might appear wrong but it's just awkward design. We always call
-        // inflateTable() when table == EMPTY_TABLE. That method will take "threshold"
-        // to mean "capacity" and then replace it with the real threshold (i.e, multiplied with
-        // the load factor).
-        threshold = initialCapacity;
-        init();
+        this.loadFactor = loadFactor;
+        this.threshold = tableSizeFor(initialCapacity);
     }
 
     /**
@@ -237,7 +472,7 @@
      * (16) and the default load factor (0.75).
      */
     public HashMap() {
-        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
     }
 
     /**
@@ -250,61 +485,35 @@
      * @throws  NullPointerException if the specified map is null
      */
     public HashMap(Map<? extends K, ? extends V> m) {
-        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
-                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
-        inflateTable(threshold);
-
-        putAllForCreate(m);
-    }
-
-    private static int roundUpToPowerOf2(int number) {
-        // assert number >= 0 : "number must be non-negative";
-        int rounded = number >= MAXIMUM_CAPACITY
-                ? MAXIMUM_CAPACITY
-                : (rounded = Integer.highestOneBit(number)) != 0
-                    ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded
-                    : 1;
-
-        return rounded;
+        this.loadFactor = DEFAULT_LOAD_FACTOR;
+        putMapEntries(m, false);
     }
 
     /**
-     * Inflates the table.
+     * Implements Map.putAll and Map constructor
+     *
+     * @param m the map
+     * @param evict false when initially constructing this map, else
+     * true (relayed to method afterNodeInsertion).
      */
-    private void inflateTable(int toSize) {
-        // Find a power of 2 >= toSize
-        int capacity = roundUpToPowerOf2(toSize);
-
-        // Android-changed: Replace usage of Math.min() here because this method is
-        // called from the <clinit> of runtime, at which point the native libraries
-        // needed by Float.* might not be loaded.
-        float thresholdFloat = capacity * loadFactor;
-        if (thresholdFloat > MAXIMUM_CAPACITY + 1) {
-            thresholdFloat = MAXIMUM_CAPACITY + 1;
+    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
+        int s = m.size();
+        if (s > 0) {
+            if (table == null) { // pre-size
+                float ft = ((float)s / loadFactor) + 1.0F;
+                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
+                         (int)ft : MAXIMUM_CAPACITY);
+                if (t > threshold)
+                    threshold = tableSizeFor(t);
+            }
+            else if (s > threshold)
+                resize();
+            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
+                K key = e.getKey();
+                V value = e.getValue();
+                putVal(hash(key), key, value, false, evict);
+            }
         }
-
-        threshold = (int) thresholdFloat;
-        table = new HashMapEntry[capacity];
-    }
-
-    // internal utilities
-
-    /**
-     * Initialization hook for subclasses. This method is called
-     * in all constructors and pseudo-constructors (clone, readObject)
-     * after HashMap has been initialized but before any entries have
-     * been inserted.  (In the absence of this method, readObject would
-     * require explicit knowledge of subclasses.)
-     */
-    void init() {
-    }
-
-    /**
-     * Returns index for hash code h.
-     */
-    static int indexFor(int h, int length) {
-        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
-        return h & (length-1);
     }
 
     /**
@@ -343,27 +552,33 @@
      * @see #put(Object, Object)
      */
     public V get(Object key) {
-        if (key == null)
-            return getForNullKey();
-        Entry<K,V> entry = getEntry(key);
-
-        return null == entry ? null : entry.getValue();
+        Node<K,V> e;
+        return (e = getNode(hash(key), key)) == null ? null : e.value;
     }
 
     /**
-     * Offloaded version of get() to look up null keys.  Null keys map
-     * to index 0.  This null case is split out into separate methods
-     * for the sake of performance in the two most commonly used
-     * operations (get and put), but incorporated with conditionals in
-     * others.
+     * Implements Map.get and related methods
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @return the node, or null if none
      */
-    private V getForNullKey() {
-        if (size == 0) {
-            return null;
-        }
-        for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
-            if (e.key == null)
-                return e.value;
+    final Node<K,V> getNode(int hash, Object key) {
+        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+            (first = tab[(n - 1) & hash]) != null) {
+            if (first.hash == hash && // always check first node
+                ((k = first.key) == key || (key != null && key.equals(k))))
+                return first;
+            if ((e = first.next) != null) {
+                if (first instanceof TreeNode)
+                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
+                do {
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k))))
+                        return e;
+                } while ((e = e.next) != null);
+            }
         }
         return null;
     }
@@ -377,29 +592,7 @@
      * key.
      */
     public boolean containsKey(Object key) {
-        return getEntry(key) != null;
-    }
-
-    /**
-     * Returns the entry associated with the specified key in the
-     * HashMap.  Returns null if the HashMap contains no mapping
-     * for the key.
-     */
-    final Entry<K,V> getEntry(Object key) {
-        if (size == 0) {
-            return null;
-        }
-
-        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
-        for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)];
-             e != null;
-             e = e.next) {
-            Object k;
-            if (e.hash == hash &&
-                ((k = e.key) == key || (key != null && key.equals(k))))
-                return e;
-        }
-        return null;
+        return getNode(hash(key), key) != null;
     }
 
     /**
@@ -415,118 +608,167 @@
      *         previously associated <tt>null</tt> with <tt>key</tt>.)
      */
     public V put(K key, V value) {
-        if (table == EMPTY_TABLE) {
-            inflateTable(threshold);
-        }
-        if (key == null)
-            return putForNullKey(value);
-        int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
-        int i = indexFor(hash, table.length);
-        for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
-            Object k;
-            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
+        return putVal(hash(key), key, value, false, true);
+    }
+
+    /**
+     * Implements Map.put and related methods
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @param value the value to put
+     * @param onlyIfAbsent if true, don't change existing value
+     * @param evict if false, the table is in creation mode.
+     * @return previous value, or null if none
+     */
+    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+                   boolean evict) {
+        Node<K,V>[] tab; Node<K,V> p; int n, i;
+        if ((tab = table) == null || (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if ((p = tab[i = (n - 1) & hash]) == null)
+            tab[i] = newNode(hash, key, value, null);
+        else {
+            Node<K,V> e; K k;
+            if (p.hash == hash &&
+                ((k = p.key) == key || (key != null && key.equals(k))))
+                e = p;
+            else if (p instanceof TreeNode)
+                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
+            else {
+                for (int binCount = 0; ; ++binCount) {
+                    if ((e = p.next) == null) {
+                        p.next = newNode(hash, key, value, null);
+                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+                            treeifyBin(tab, hash);
+                        break;
+                    }
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k))))
+                        break;
+                    p = e;
+                }
+            }
+            if (e != null) { // existing mapping for key
                 V oldValue = e.value;
-                e.value = value;
-                e.recordAccess(this);
+                if (!onlyIfAbsent || oldValue == null)
+                    e.value = value;
+                afterNodeAccess(e);
                 return oldValue;
             }
         }
-
-        modCount++;
-        addEntry(hash, key, value, i);
+        ++modCount;
+        if (++size > threshold)
+            resize();
+        afterNodeInsertion(evict);
         return null;
     }
 
     /**
-     * Offloaded version of put for null keys
-     */
-    private V putForNullKey(V value) {
-        for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
-            if (e.key == null) {
-                V oldValue = e.value;
-                e.value = value;
-                e.recordAccess(this);
-                return oldValue;
-            }
-        }
-        modCount++;
-        addEntry(0, null, value, 0);
-        return null;
-    }
-
-    /**
-     * This method is used instead of put by constructors and
-     * pseudoconstructors (clone, readObject).  It does not resize the table,
-     * check for comodification, etc.  It calls createEntry rather than
-     * addEntry.
-     */
-    private void putForCreate(K key, V value) {
-        int hash = null == key ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
-        int i = indexFor(hash, table.length);
-
-        /**
-         * Look for preexisting entry for key.  This will never happen for
-         * clone or deserialize.  It will only happen for construction if the
-         * input Map is a sorted map whose ordering is inconsistent w/ equals.
-         */
-        for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
-            Object k;
-            if (e.hash == hash &&
-                ((k = e.key) == key || (key != null && key.equals(k)))) {
-                e.value = value;
-                return;
-            }
-        }
-
-        createEntry(hash, key, value, i);
-    }
-
-    private void putAllForCreate(Map<? extends K, ? extends V> m) {
-        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
-            putForCreate(e.getKey(), e.getValue());
-    }
-
-    /**
-     * Rehashes the contents of this map into a new array with a
-     * larger capacity.  This method is called automatically when the
-     * number of keys in this map reaches its threshold.
+     * Initializes or doubles table size.  If null, allocates in
+     * accord with initial capacity target held in field threshold.
+     * Otherwise, because we are using power-of-two expansion, the
+     * elements from each bin must either stay at same index, or move
+     * with a power of two offset in the new table.
      *
-     * If current capacity is MAXIMUM_CAPACITY, this method does not
-     * resize the map, but sets threshold to Integer.MAX_VALUE.
-     * This has the effect of preventing future calls.
-     *
-     * @param newCapacity the new capacity, MUST be a power of two;
-     *        must be greater than current capacity unless current
-     *        capacity is MAXIMUM_CAPACITY (in which case value
-     *        is irrelevant).
+     * @return the table
      */
-    void resize(int newCapacity) {
-        HashMapEntry[] oldTable = table;
-        int oldCapacity = oldTable.length;
-        if (oldCapacity == MAXIMUM_CAPACITY) {
-            threshold = Integer.MAX_VALUE;
-            return;
+    final Node<K,V>[] resize() {
+        Node<K,V>[] oldTab = table;
+        int oldCap = (oldTab == null) ? 0 : oldTab.length;
+        int oldThr = threshold;
+        int newCap, newThr = 0;
+        if (oldCap > 0) {
+            if (oldCap >= MAXIMUM_CAPACITY) {
+                threshold = Integer.MAX_VALUE;
+                return oldTab;
+            }
+            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+                     oldCap >= DEFAULT_INITIAL_CAPACITY)
+                newThr = oldThr << 1; // double threshold
         }
-
-        HashMapEntry[] newTable = new HashMapEntry[newCapacity];
-        transfer(newTable);
-        table = newTable;
-        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
+        else if (oldThr > 0) // initial capacity was placed in threshold
+            newCap = oldThr;
+        else {               // zero initial threshold signifies using defaults
+            newCap = DEFAULT_INITIAL_CAPACITY;
+            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+        }
+        if (newThr == 0) {
+            float ft = (float)newCap * loadFactor;
+            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+                      (int)ft : Integer.MAX_VALUE);
+        }
+        threshold = newThr;
+        @SuppressWarnings({"rawtypes","unchecked"})
+            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
+        table = newTab;
+        if (oldTab != null) {
+            for (int j = 0; j < oldCap; ++j) {
+                Node<K,V> e;
+                if ((e = oldTab[j]) != null) {
+                    oldTab[j] = null;
+                    if (e.next == null)
+                        newTab[e.hash & (newCap - 1)] = e;
+                    else if (e instanceof TreeNode)
+                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
+                    else { // preserve order
+                        Node<K,V> loHead = null, loTail = null;
+                        Node<K,V> hiHead = null, hiTail = null;
+                        Node<K,V> next;
+                        do {
+                            next = e.next;
+                            if ((e.hash & oldCap) == 0) {
+                                if (loTail == null)
+                                    loHead = e;
+                                else
+                                    loTail.next = e;
+                                loTail = e;
+                            }
+                            else {
+                                if (hiTail == null)
+                                    hiHead = e;
+                                else
+                                    hiTail.next = e;
+                                hiTail = e;
+                            }
+                        } while ((e = next) != null);
+                        if (loTail != null) {
+                            loTail.next = null;
+                            newTab[j] = loHead;
+                        }
+                        if (hiTail != null) {
+                            hiTail.next = null;
+                            newTab[j + oldCap] = hiHead;
+                        }
+                    }
+                }
+            }
+        }
+        return newTab;
     }
 
     /**
-     * Transfers all entries from current table to newTable.
+     * Replaces all linked nodes in bin at index for given hash unless
+     * table is too small, in which case resizes instead.
      */
-    void transfer(HashMapEntry[] newTable) {
-        int newCapacity = newTable.length;
-        for (HashMapEntry<K,V> e : table) {
-            while(null != e) {
-                HashMapEntry<K,V> next = e.next;
-                int i = indexFor(e.hash, newCapacity);
-                e.next = newTable[i];
-                newTable[i] = e;
-                e = next;
-            }
+    final void treeifyBin(Node<K,V>[] tab, int hash) {
+        int n, index; Node<K,V> e;
+        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+            resize();
+        else if ((e = tab[index = (n - 1) & hash]) != null) {
+            TreeNode<K,V> hd = null, tl = null;
+            do {
+                TreeNode<K,V> p = replacementTreeNode(e, null);
+                if (tl == null)
+                    hd = p;
+                else {
+                    p.prev = tl;
+                    tl.next = p;
+                }
+                tl = p;
+            } while ((e = e.next) != null);
+            if ((tab[index] = hd) != null)
+                hd.treeify(tab);
         }
     }
 
@@ -539,36 +781,7 @@
      * @throws NullPointerException if the specified map is null
      */
     public void putAll(Map<? extends K, ? extends V> m) {
-        int numKeysToBeAdded = m.size();
-        if (numKeysToBeAdded == 0)
-            return;
-
-        if (table == EMPTY_TABLE) {
-            inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
-        }
-
-        /*
-         * Expand the map if the map if the number of mappings to be added
-         * is greater than or equal to threshold.  This is conservative; the
-         * obvious condition is (m.size() + size) >= threshold, but this
-         * condition could result in a map with twice the appropriate capacity,
-         * if the keys to be added overlap with the keys already in this map.
-         * By using the conservative calculation, we subject ourself
-         * to at most one extra resize.
-         */
-        if (numKeysToBeAdded > threshold) {
-            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
-            if (targetCapacity > MAXIMUM_CAPACITY)
-                targetCapacity = MAXIMUM_CAPACITY;
-            int newCapacity = table.length;
-            while (newCapacity < targetCapacity)
-                newCapacity <<= 1;
-            if (newCapacity > table.length)
-                resize(newCapacity);
-        }
-
-        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
-            put(e.getKey(), e.getValue());
+        putMapEntries(m, true);
     }
 
     /**
@@ -581,77 +794,60 @@
      *         previously associated <tt>null</tt> with <tt>key</tt>.)
      */
     public V remove(Object key) {
-        Entry<K,V> e = removeEntryForKey(key);
-        return (e == null ? null : e.getValue());
+        Node<K,V> e;
+        return (e = removeNode(hash(key), key, null, false, true)) == null ?
+            null : e.value;
     }
 
     /**
-     * Removes and returns the entry associated with the specified key
-     * in the HashMap.  Returns null if the HashMap contains no mapping
-     * for this key.
+     * Implements Map.remove and related methods
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @param value the value to match if matchValue, else ignored
+     * @param matchValue if true only remove if value is equal
+     * @param movable if false do not move other nodes while removing
+     * @return the node, or null if none
      */
-    final Entry<K,V> removeEntryForKey(Object key) {
-        if (size == 0) {
-            return null;
-        }
-        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
-        int i = indexFor(hash, table.length);
-        HashMapEntry<K,V> prev = table[i];
-        HashMapEntry<K,V> e = prev;
-
-        while (e != null) {
-            HashMapEntry<K,V> next = e.next;
-            Object k;
-            if (e.hash == hash &&
-                ((k = e.key) == key || (key != null && key.equals(k)))) {
-                modCount++;
-                size--;
-                if (prev == e)
-                    table[i] = next;
-                else
-                    prev.next = next;
-                e.recordRemoval(this);
-                return e;
+    final Node<K,V> removeNode(int hash, Object key, Object value,
+                               boolean matchValue, boolean movable) {
+        Node<K,V>[] tab; Node<K,V> p; int n, index;
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+            (p = tab[index = (n - 1) & hash]) != null) {
+            Node<K,V> node = null, e; K k; V v;
+            if (p.hash == hash &&
+                ((k = p.key) == key || (key != null && key.equals(k))))
+                node = p;
+            else if ((e = p.next) != null) {
+                if (p instanceof TreeNode)
+                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
+                else {
+                    do {
+                        if (e.hash == hash &&
+                            ((k = e.key) == key ||
+                             (key != null && key.equals(k)))) {
+                            node = e;
+                            break;
+                        }
+                        p = e;
+                    } while ((e = e.next) != null);
+                }
             }
-            prev = e;
-            e = next;
-        }
-
-        return e;
-    }
-
-    /**
-     * Special version of remove for EntrySet using {@code Map.Entry.equals()}
-     * for matching.
-     */
-    final Entry<K,V> removeMapping(Object o) {
-        if (size == 0 || !(o instanceof Map.Entry))
-            return null;
-
-        Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
-        Object key = entry.getKey();
-        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
-        int i = indexFor(hash, table.length);
-        HashMapEntry<K,V> prev = table[i];
-        HashMapEntry<K,V> e = prev;
-
-        while (e != null) {
-            HashMapEntry<K,V> next = e.next;
-            if (e.hash == hash && e.equals(entry)) {
-                modCount++;
-                size--;
-                if (prev == e)
-                    table[i] = next;
+            if (node != null && (!matchValue || (v = node.value) == value ||
+                                 (value != null && value.equals(v)))) {
+                if (node instanceof TreeNode)
+                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
+                else if (node == p)
+                    tab[index] = node.next;
                 else
-                    prev.next = next;
-                e.recordRemoval(this);
-                return e;
+                    p.next = node.next;
+                ++modCount;
+                --size;
+                afterNodeRemoval(node);
+                return node;
             }
-            prev = e;
-            e = next;
         }
-
-        return e;
+        return null;
     }
 
     /**
@@ -659,9 +855,13 @@
      * The map will be empty after this call returns.
      */
     public void clear() {
+        Node<K,V>[] tab;
         modCount++;
-        Arrays.fill(table, null);
-        size = 0;
+        if ((tab = table) != null && size > 0) {
+            size = 0;
+            for (int i = 0; i < tab.length; ++i)
+                tab[i] = null;
+        }
     }
 
     /**
@@ -673,172 +873,552 @@
      *         specified value
      */
     public boolean containsValue(Object value) {
-        if (value == null)
-            return containsNullValue();
-
-        HashMapEntry[] tab = table;
-        for (int i = 0; i < tab.length ; i++)
-            for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
-                if (value.equals(e.value))
-                    return true;
+        Node<K,V>[] tab; V v;
+        if ((tab = table) != null && size > 0) {
+            for (int i = 0; i < tab.length; ++i) {
+                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+                    if ((v = e.value) == value ||
+                        (value != null && value.equals(v)))
+                        return true;
+                }
+            }
+        }
         return false;
     }
 
     /**
-     * Special-case code for containsValue with null argument
+     * Returns a {@link Set} view of the keys contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own <tt>remove</tt> operation), the results of
+     * the iteration are undefined.  The set supports element removal,
+     * which removes the corresponding mapping from the map, via the
+     * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+     * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
+     * operations.
+     *
+     * @return a set view of the keys contained in this map
      */
-    private boolean containsNullValue() {
-        HashMapEntry[] tab = table;
-        for (int i = 0; i < tab.length ; i++)
-            for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
-                if (e.value == null)
-                    return true;
+    public Set<K> keySet() {
+        Set<K> ks;
+        return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
+    }
+
+    final class KeySet extends AbstractSet<K> {
+        public final int size()                 { return size; }
+        public final void clear()               { HashMap.this.clear(); }
+        public final Iterator<K> iterator()     { return new KeyIterator(); }
+        public final boolean contains(Object o) { return containsKey(o); }
+        public final boolean remove(Object key) {
+            return removeNode(hash(key), key, null, false, true) != null;
+        }
+        public final Spliterator<K> spliterator() {
+            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
+        }
+        public final void forEach(Consumer<? super K> action) {
+            Node<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                // Android-changed: Detect changes to modCount early.
+                for (int i = 0; (i < tab.length && modCount == mc); ++i) {
+                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
+                        action.accept(e.key);
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this map.
+     * The collection is backed by the map, so changes to the map are
+     * reflected in the collection, and vice-versa.  If the map is
+     * modified while an iteration over the collection is in progress
+     * (except through the iterator's own <tt>remove</tt> operation),
+     * the results of the iteration are undefined.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the <tt>Iterator.remove</tt>,
+     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
+     * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
+     * support the <tt>add</tt> or <tt>addAll</tt> operations.
+     *
+     * @return a view of the values contained in this map
+     */
+    public Collection<V> values() {
+        Collection<V> vs;
+        return (vs = values) == null ? (values = new Values()) : vs;
+    }
+
+    final class Values extends AbstractCollection<V> {
+        public final int size()                 { return size; }
+        public final void clear()               { HashMap.this.clear(); }
+        public final Iterator<V> iterator()     { return new ValueIterator(); }
+        public final boolean contains(Object o) { return containsValue(o); }
+        public final Spliterator<V> spliterator() {
+            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
+        }
+        public final void forEach(Consumer<? super V> action) {
+            Node<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                // Android-changed: Detect changes to modCount early.
+                for (int i = 0; (i < tab.length && modCount == mc); ++i) {
+                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
+                        action.accept(e.value);
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Returns a {@link Set} view of the mappings contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own <tt>remove</tt> operation, or through the
+     * <tt>setValue</tt> operation on a map entry returned by the
+     * iterator) the results of the iteration are undefined.  The set
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the <tt>Iterator.remove</tt>,
+     * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
+     * <tt>clear</tt> operations.  It does not support the
+     * <tt>add</tt> or <tt>addAll</tt> operations.
+     *
+     * @return a set view of the mappings contained in this map
+     */
+    public Set<Map.Entry<K,V>> entrySet() {
+        Set<Map.Entry<K,V>> es;
+        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
+    }
+
+    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
+        public final int size()                 { return size; }
+        public final void clear()               { HashMap.this.clear(); }
+        public final Iterator<Map.Entry<K,V>> iterator() {
+            return new EntryIterator();
+        }
+        public final boolean contains(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+            Object key = e.getKey();
+            Node<K,V> candidate = getNode(hash(key), key);
+            return candidate != null && candidate.equals(e);
+        }
+        public final boolean remove(Object o) {
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+                Object key = e.getKey();
+                Object value = e.getValue();
+                return removeNode(hash(key), key, value, true, true) != null;
+            }
+            return false;
+        }
+        public final Spliterator<Map.Entry<K,V>> spliterator() {
+            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
+        }
+        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+            Node<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                // Android-changed: Detect changes to modCount early.
+                for (int i = 0; (i < tab.length && modCount == mc); ++i) {
+                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
+                        action.accept(e);
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    // Overrides of JDK8 Map extension methods
+
+    @Override
+    public V getOrDefault(Object key, V defaultValue) {
+        Node<K,V> e;
+        return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
+    }
+
+    @Override
+    public V putIfAbsent(K key, V value) {
+        return putVal(hash(key), key, value, true, true);
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        return removeNode(hash(key), key, value, true, true) != null;
+    }
+
+    @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+        Node<K,V> e; V v;
+        if ((e = getNode(hash(key), key)) != null &&
+            ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
+            e.value = newValue;
+            afterNodeAccess(e);
+            return true;
+        }
         return false;
     }
 
+    @Override
+    public V replace(K key, V value) {
+        Node<K,V> e;
+        if ((e = getNode(hash(key), key)) != null) {
+            V oldValue = e.value;
+            e.value = value;
+            afterNodeAccess(e);
+            return oldValue;
+        }
+        return null;
+    }
+
+    @Override
+    public V computeIfAbsent(K key,
+                             Function<? super K, ? extends V> mappingFunction) {
+        if (mappingFunction == null)
+            throw new NullPointerException();
+        int hash = hash(key);
+        Node<K,V>[] tab; Node<K,V> first; int n, i;
+        int binCount = 0;
+        TreeNode<K,V> t = null;
+        Node<K,V> old = null;
+        if (size > threshold || (tab = table) == null ||
+            (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if ((first = tab[i = (n - 1) & hash]) != null) {
+            if (first instanceof TreeNode)
+                old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+            else {
+                Node<K,V> e = first; K k;
+                do {
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k)))) {
+                        old = e;
+                        break;
+                    }
+                    ++binCount;
+                } while ((e = e.next) != null);
+            }
+            V oldValue;
+            if (old != null && (oldValue = old.value) != null) {
+                afterNodeAccess(old);
+                return oldValue;
+            }
+        }
+        V v = mappingFunction.apply(key);
+        if (v == null) {
+            return null;
+        } else if (old != null) {
+            old.value = v;
+            afterNodeAccess(old);
+            return v;
+        }
+        else if (t != null)
+            t.putTreeVal(this, tab, hash, key, v);
+        else {
+            tab[i] = newNode(hash, key, v, first);
+            if (binCount >= TREEIFY_THRESHOLD - 1)
+                treeifyBin(tab, hash);
+        }
+        ++modCount;
+        ++size;
+        afterNodeInsertion(true);
+        return v;
+    }
+
+    public V computeIfPresent(K key,
+                              BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (remappingFunction == null)
+            throw new NullPointerException();
+        Node<K,V> e; V oldValue;
+        int hash = hash(key);
+        if ((e = getNode(hash, key)) != null &&
+            (oldValue = e.value) != null) {
+            V v = remappingFunction.apply(key, oldValue);
+            if (v != null) {
+                e.value = v;
+                afterNodeAccess(e);
+                return v;
+            }
+            else
+                removeNode(hash, key, null, false, true);
+        }
+        return null;
+    }
+
+    @Override
+    public V compute(K key,
+                     BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (remappingFunction == null)
+            throw new NullPointerException();
+        int hash = hash(key);
+        Node<K,V>[] tab; Node<K,V> first; int n, i;
+        int binCount = 0;
+        TreeNode<K,V> t = null;
+        Node<K,V> old = null;
+        if (size > threshold || (tab = table) == null ||
+            (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if ((first = tab[i = (n - 1) & hash]) != null) {
+            if (first instanceof TreeNode)
+                old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+            else {
+                Node<K,V> e = first; K k;
+                do {
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k)))) {
+                        old = e;
+                        break;
+                    }
+                    ++binCount;
+                } while ((e = e.next) != null);
+            }
+        }
+        V oldValue = (old == null) ? null : old.value;
+        V v = remappingFunction.apply(key, oldValue);
+        if (old != null) {
+            if (v != null) {
+                old.value = v;
+                afterNodeAccess(old);
+            }
+            else
+                removeNode(hash, key, null, false, true);
+        }
+        else if (v != null) {
+            if (t != null)
+                t.putTreeVal(this, tab, hash, key, v);
+            else {
+                tab[i] = newNode(hash, key, v, first);
+                if (binCount >= TREEIFY_THRESHOLD - 1)
+                    treeifyBin(tab, hash);
+            }
+            ++modCount;
+            ++size;
+            afterNodeInsertion(true);
+        }
+        return v;
+    }
+
+    @Override
+    public V merge(K key, V value,
+                   BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+        if (value == null)
+            throw new NullPointerException();
+        if (remappingFunction == null)
+            throw new NullPointerException();
+        int hash = hash(key);
+        Node<K,V>[] tab; Node<K,V> first; int n, i;
+        int binCount = 0;
+        TreeNode<K,V> t = null;
+        Node<K,V> old = null;
+        if (size > threshold || (tab = table) == null ||
+            (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if ((first = tab[i = (n - 1) & hash]) != null) {
+            if (first instanceof TreeNode)
+                old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+            else {
+                Node<K,V> e = first; K k;
+                do {
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k)))) {
+                        old = e;
+                        break;
+                    }
+                    ++binCount;
+                } while ((e = e.next) != null);
+            }
+        }
+        if (old != null) {
+            V v;
+            if (old.value != null)
+                v = remappingFunction.apply(old.value, value);
+            else
+                v = value;
+            if (v != null) {
+                old.value = v;
+                afterNodeAccess(old);
+            }
+            else
+                removeNode(hash, key, null, false, true);
+            return v;
+        }
+        if (value != null) {
+            if (t != null)
+                t.putTreeVal(this, tab, hash, key, value);
+            else {
+                tab[i] = newNode(hash, key, value, first);
+                if (binCount >= TREEIFY_THRESHOLD - 1)
+                    treeifyBin(tab, hash);
+            }
+            ++modCount;
+            ++size;
+            afterNodeInsertion(true);
+        }
+        return value;
+    }
+
+    @Override
+    public void forEach(BiConsumer<? super K, ? super V> action) {
+        Node<K,V>[] tab;
+        if (action == null)
+            throw new NullPointerException();
+        if (size > 0 && (tab = table) != null) {
+            int mc = modCount;
+            // Android-changed: Detect changes to modCount early.
+            for (int i = 0; (i < tab.length && mc == modCount); ++i) {
+                for (Node<K,V> e = tab[i]; e != null; e = e.next)
+                    action.accept(e.key, e.value);
+            }
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    @Override
+    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+        Node<K,V>[] tab;
+        if (function == null)
+            throw new NullPointerException();
+        if (size > 0 && (tab = table) != null) {
+            int mc = modCount;
+            for (int i = 0; i < tab.length; ++i) {
+                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+                    e.value = function.apply(e.key, e.value);
+                }
+            }
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    // Cloning and serialization
+
     /**
      * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
      * values themselves are not cloned.
      *
      * @return a shallow copy of this map
      */
+    @SuppressWarnings("unchecked")
+    @Override
     public Object clone() {
-        HashMap<K,V> result = null;
+        HashMap<K,V> result;
         try {
             result = (HashMap<K,V>)super.clone();
         } catch (CloneNotSupportedException e) {
-            // assert false;
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError(e);
         }
-        if (result.table != EMPTY_TABLE) {
-            result.inflateTable(Math.min(
-                (int) Math.min(
-                    size * Math.min(1 / loadFactor, 4.0f),
-                    // we have limits...
-                    HashMap.MAXIMUM_CAPACITY),
-               table.length));
-        }
-        result.entrySet = null;
-        result.modCount = 0;
-        result.size = 0;
-        result.init();
-        result.putAllForCreate(this);
-
+        result.reinitialize();
+        result.putMapEntries(this, false);
         return result;
     }
 
-    /** @hide */  // Android added.
-    static class HashMapEntry<K,V> implements Map.Entry<K,V> {
-        final K key;
-        V value;
-        HashMapEntry<K,V> next;
-        int hash;
+    // These methods are also used when serializing HashSets
+    final float loadFactor() { return loadFactor; }
+    final int capacity() {
+        return (table != null) ? table.length :
+            (threshold > 0) ? threshold :
+            DEFAULT_INITIAL_CAPACITY;
+    }
 
-        /**
-         * Creates new entry.
-         */
-        HashMapEntry(int h, K k, V v, HashMapEntry<K,V> n) {
-            value = v;
-            next = n;
-            key = k;
-            hash = h;
-        }
+    /**
+     * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
+     * serialize it).
+     *
+     * @serialData The <i>capacity</i> of the HashMap (the length of the
+     *             bucket array) is emitted (int), followed by the
+     *             <i>size</i> (an int, the number of key-value
+     *             mappings), followed by the key (Object) and value (Object)
+     *             for each key-value mapping.  The key-value mappings are
+     *             emitted in no particular order.
+     */
+    private void writeObject(java.io.ObjectOutputStream s)
+        throws IOException {
+        int buckets = capacity();
+        // Write out the threshold, loadfactor, and any hidden stuff
+        s.defaultWriteObject();
+        s.writeInt(buckets);
+        s.writeInt(size);
+        internalWriteEntries(s);
+    }
 
-        public final K getKey() {
-            return key;
-        }
+    /**
+     * Reconstitute the {@code HashMap} instance from a stream (i.e.,
+     * deserialize it).
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws IOException, ClassNotFoundException {
+        // Read in the threshold (ignored), loadfactor, and any hidden stuff
+        s.defaultReadObject();
+        reinitialize();
+        if (loadFactor <= 0 || Float.isNaN(loadFactor))
+            throw new InvalidObjectException("Illegal load factor: " +
+                                             loadFactor);
+        s.readInt();                // Read and ignore number of buckets
+        int mappings = s.readInt(); // Read number of mappings (size)
+        if (mappings < 0)
+            throw new InvalidObjectException("Illegal mappings count: " +
+                                             mappings);
+        else if (mappings > 0) { // (if zero, use defaults)
+            // Size the table using given load factor only if within
+            // range of 0.25...4.0
+            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
+            float fc = (float)mappings / lf + 1.0f;
+            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
+                       DEFAULT_INITIAL_CAPACITY :
+                       (fc >= MAXIMUM_CAPACITY) ?
+                       MAXIMUM_CAPACITY :
+                       tableSizeFor((int)fc));
+            float ft = (float)cap * lf;
+            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
+                         (int)ft : Integer.MAX_VALUE);
+            @SuppressWarnings({"rawtypes","unchecked"})
+                Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
+            table = tab;
 
-        public final V getValue() {
-            return value;
-        }
-
-        public final V setValue(V newValue) {
-            V oldValue = value;
-            value = newValue;
-            return oldValue;
-        }
-
-        public final boolean equals(Object o) {
-            if (!(o instanceof Map.Entry))
-                return false;
-            Map.Entry e = (Map.Entry)o;
-            Object k1 = getKey();
-            Object k2 = e.getKey();
-            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
-                Object v1 = getValue();
-                Object v2 = e.getValue();
-                if (v1 == v2 || (v1 != null && v1.equals(v2)))
-                    return true;
+            // Read the keys and values, and put the mappings in the HashMap
+            for (int i = 0; i < mappings; i++) {
+                @SuppressWarnings("unchecked")
+                    K key = (K) s.readObject();
+                @SuppressWarnings("unchecked")
+                    V value = (V) s.readObject();
+                putVal(hash(key), key, value, false, false);
             }
-            return false;
-        }
-
-        public final int hashCode() {
-            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
-        }
-
-        public final String toString() {
-            return getKey() + "=" + getValue();
-        }
-
-        /**
-         * This method is invoked whenever the value in an entry is
-         * overwritten by an invocation of put(k,v) for a key k that's already
-         * in the HashMap.
-         */
-        void recordAccess(HashMap<K,V> m) {
-        }
-
-        /**
-         * This method is invoked whenever the entry is
-         * removed from the table.
-         */
-        void recordRemoval(HashMap<K,V> m) {
         }
     }
 
-    /**
-     * Adds a new entry with the specified key, value and hash code to
-     * the specified bucket.  It is the responsibility of this
-     * method to resize the table if appropriate.
-     *
-     * Subclass overrides this to alter the behavior of put method.
-     */
-    void addEntry(int hash, K key, V value, int bucketIndex) {
-        if ((size >= threshold) && (null != table[bucketIndex])) {
-            resize(2 * table.length);
-            hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
-            bucketIndex = indexFor(hash, table.length);
-        }
+    /* ------------------------------------------------------------ */
+    // iterators
 
-        createEntry(hash, key, value, bucketIndex);
-    }
-
-    /**
-     * Like addEntry except that this version is used when creating entries
-     * as part of Map construction or "pseudo-construction" (cloning,
-     * deserialization).  This version needn't worry about resizing the table.
-     *
-     * Subclass overrides this to alter the behavior of HashMap(Map),
-     * clone, and readObject.
-     */
-    void createEntry(int hash, K key, V value, int bucketIndex) {
-        HashMapEntry<K,V> e = table[bucketIndex];
-        table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
-        size++;
-    }
-
-    private abstract class HashIterator<E> implements Iterator<E> {
-        HashMapEntry<K,V> next;        // next entry to return
-        int expectedModCount;   // For fast-fail
-        int index;              // current slot
-        HashMapEntry<K,V> current;     // current entry
+    abstract class HashIterator {
+        Node<K,V> next;        // next entry to return
+        Node<K,V> current;     // current entry
+        int expectedModCount;  // for fast-fail
+        int index;             // current slot
 
         HashIterator() {
             expectedModCount = modCount;
-            if (size > 0) { // advance to first entry
-                HashMapEntry[] t = table;
-                while (index < t.length && (next = t[index++]) == null)
-                    ;
+            Node<K,V>[] t = table;
+            current = next = null;
+            index = 0;
+            if (t != null && size > 0) { // advance to first entry
+                do {} while (index < t.length && (next = t[index++]) == null);
             }
         }
 
@@ -846,58 +1426,53 @@
             return next != null;
         }
 
-        final Entry<K,V> nextEntry() {
+        final Node<K,V> nextNode() {
+            Node<K,V>[] t;
+            Node<K,V> e = next;
             if (modCount != expectedModCount)
                 throw new ConcurrentModificationException();
-            HashMapEntry<K,V> e = next;
             if (e == null)
                 throw new NoSuchElementException();
-
-            if ((next = e.next) == null) {
-                HashMapEntry[] t = table;
-                while (index < t.length && (next = t[index++]) == null)
-                    ;
+            if ((next = (current = e).next) == null && (t = table) != null) {
+                do {} while (index < t.length && (next = t[index++]) == null);
             }
-            current = e;
             return e;
         }
 
-        public void remove() {
-            if (current == null)
+        public final void remove() {
+            Node<K,V> p = current;
+            if (p == null)
                 throw new IllegalStateException();
             if (modCount != expectedModCount)
                 throw new ConcurrentModificationException();
-            Object k = current.key;
             current = null;
-            HashMap.this.removeEntryForKey(k);
+            K key = p.key;
+            removeNode(hash(key), key, null, false, false);
             expectedModCount = modCount;
         }
     }
 
-    private final class ValueIterator extends HashIterator<V> {
-        public V next() {
-            return nextEntry().getValue();
-        }
+    final class KeyIterator extends HashIterator
+        implements Iterator<K> {
+        public final K next() { return nextNode().key; }
     }
 
-    private final class KeyIterator extends HashIterator<K> {
-        public K next() {
-            return nextEntry().getKey();
-        }
+    final class ValueIterator extends HashIterator
+        implements Iterator<V> {
+        public final V next() { return nextNode().value; }
     }
 
-    private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
-        public Map.Entry<K,V> next() {
-            return nextEntry();
-        }
+    final class EntryIterator extends HashIterator
+        implements Iterator<Map.Entry<K,V>> {
+        public final Map.Entry<K,V> next() { return nextNode(); }
     }
 
-        /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
     // spliterators
 
     static class HashMapSpliterator<K,V> {
         final HashMap<K,V> map;
-        HashMapEntry<K,V> current;  // current entry
+        Node<K,V> current;          // current node
         int index;                  // current index, modified on advance/split
         int fence;                  // one past last index
         int est;                    // size estimate
@@ -919,7 +1494,7 @@
                 HashMap<K,V> m = map;
                 est = m.size;
                 expectedModCount = m.modCount;
-                HashMapEntry<K,V>[] tab = m.table;
+                Node<K,V>[] tab = m.table;
                 hi = fence = (tab == null) ? 0 : tab.length;
             }
             return hi;
@@ -932,8 +1507,8 @@
     }
 
     static final class KeySpliterator<K,V>
-            extends HashMapSpliterator<K,V>
-            implements Spliterator<K> {
+        extends HashMapSpliterator<K,V>
+        implements Spliterator<K> {
         KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
                        int expectedModCount) {
             super(m, origin, fence, est, expectedModCount);
@@ -942,8 +1517,8 @@
         public KeySpliterator<K,V> trySplit() {
             int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
             return (lo >= mid || current != null) ? null :
-                    new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
-                            expectedModCount);
+                new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
+                                        expectedModCount);
         }
 
         public void forEachRemaining(Consumer<? super K> action) {
@@ -951,7 +1526,7 @@
             if (action == null)
                 throw new NullPointerException();
             HashMap<K,V> m = map;
-            HashMapEntry<K,V>[] tab = m.table;
+            Node<K,V>[] tab = m.table;
             if ((hi = fence) < 0) {
                 mc = expectedModCount = m.modCount;
                 hi = fence = (tab == null) ? 0 : tab.length;
@@ -959,8 +1534,8 @@
             else
                 mc = expectedModCount;
             if (tab != null && tab.length >= hi &&
-                    (i = index) >= 0 && (i < (index = hi) || current != null)) {
-                HashMapEntry<K,V> p = current;
+                (i = index) >= 0 && (i < (index = hi) || current != null)) {
+                Node<K,V> p = current;
                 current = null;
                 do {
                     if (p == null)
@@ -979,7 +1554,7 @@
             int hi;
             if (action == null)
                 throw new NullPointerException();
-            HashMapEntry<K,V>[] tab = map.table;
+            Node<K,V>[] tab = map.table;
             if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
                 while (current != null || index < hi) {
                     if (current == null)
@@ -999,14 +1574,13 @@
 
         public int characteristics() {
             return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
-                    Spliterator.DISTINCT |
-                    ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+                Spliterator.DISTINCT;
         }
     }
 
     static final class ValueSpliterator<K,V>
-            extends HashMapSpliterator<K,V>
-            implements Spliterator<V> {
+        extends HashMapSpliterator<K,V>
+        implements Spliterator<V> {
         ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
                          int expectedModCount) {
             super(m, origin, fence, est, expectedModCount);
@@ -1015,8 +1589,8 @@
         public ValueSpliterator<K,V> trySplit() {
             int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
             return (lo >= mid || current != null) ? null :
-                    new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
-                            expectedModCount);
+                new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
+                                          expectedModCount);
         }
 
         public void forEachRemaining(Consumer<? super V> action) {
@@ -1024,7 +1598,7 @@
             if (action == null)
                 throw new NullPointerException();
             HashMap<K,V> m = map;
-            HashMapEntry<K,V>[] tab = m.table;
+            Node<K,V>[] tab = m.table;
             if ((hi = fence) < 0) {
                 mc = expectedModCount = m.modCount;
                 hi = fence = (tab == null) ? 0 : tab.length;
@@ -1032,8 +1606,8 @@
             else
                 mc = expectedModCount;
             if (tab != null && tab.length >= hi &&
-                    (i = index) >= 0 && (i < (index = hi) || current != null)) {
-                HashMapEntry<K,V> p = current;
+                (i = index) >= 0 && (i < (index = hi) || current != null)) {
+                Node<K,V> p = current;
                 current = null;
                 do {
                     if (p == null)
@@ -1052,7 +1626,7 @@
             int hi;
             if (action == null)
                 throw new NullPointerException();
-            HashMapEntry<K,V>[] tab = map.table;
+            Node<K,V>[] tab = map.table;
             if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
                 while (current != null || index < hi) {
                     if (current == null)
@@ -1071,14 +1645,13 @@
         }
 
         public int characteristics() {
-            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
-                    ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+            return (fence < 0 || est == map.size ? Spliterator.SIZED : 0);
         }
     }
 
     static final class EntrySpliterator<K,V>
-            extends HashMapSpliterator<K,V>
-            implements Spliterator<Map.Entry<K,V>> {
+        extends HashMapSpliterator<K,V>
+        implements Spliterator<Map.Entry<K,V>> {
         EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est,
                          int expectedModCount) {
             super(m, origin, fence, est, expectedModCount);
@@ -1087,8 +1660,8 @@
         public EntrySpliterator<K,V> trySplit() {
             int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
             return (lo >= mid || current != null) ? null :
-                    new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
-                            expectedModCount);
+                new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
+                                          expectedModCount);
         }
 
         public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
@@ -1096,7 +1669,7 @@
             if (action == null)
                 throw new NullPointerException();
             HashMap<K,V> m = map;
-            HashMapEntry<K,V>[] tab = m.table;
+            Node<K,V>[] tab = m.table;
             if ((hi = fence) < 0) {
                 mc = expectedModCount = m.modCount;
                 hi = fence = (tab == null) ? 0 : tab.length;
@@ -1104,8 +1677,8 @@
             else
                 mc = expectedModCount;
             if (tab != null && tab.length >= hi &&
-                    (i = index) >= 0 && (i < (index = hi) || current != null)) {
-                HashMapEntry<K,V> p = current;
+                (i = index) >= 0 && (i < (index = hi) || current != null)) {
+                Node<K,V> p = current;
                 current = null;
                 do {
                     if (p == null)
@@ -1124,13 +1697,13 @@
             int hi;
             if (action == null)
                 throw new NullPointerException();
-            HashMapEntry<K,V>[] tab = map.table;
+            Node<K,V>[] tab = map.table;
             if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
                 while (current != null || index < hi) {
                     if (current == null)
                         current = tab[index++];
                     else {
-                        HashMapEntry<K,V> e = current;
+                        Node<K,V> e = current;
                         current = current.next;
                         action.accept(e);
                         if (map.modCount != expectedModCount)
@@ -1144,346 +1717,667 @@
 
         public int characteristics() {
             return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
-                    Spliterator.DISTINCT |
-                    ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+                Spliterator.DISTINCT;
         }
     }
 
-    // Subclass overrides these to alter behavior of views' iterator() method
-    Iterator<K> newKeyIterator()   {
-        return new KeyIterator();
-    }
-    Iterator<V> newValueIterator()   {
-        return new ValueIterator();
-    }
-    Iterator<Map.Entry<K,V>> newEntryIterator()   {
-        return new EntryIterator();
-    }
+    /* ------------------------------------------------------------ */
+    // LinkedHashMap support
 
 
-    // Views
-
-    private transient Set<Map.Entry<K,V>> entrySet = null;
-
-    /**
-     * Returns a {@link Set} view of the keys contained in this map.
-     * The set is backed by the map, so changes to the map are
-     * reflected in the set, and vice-versa.  If the map is modified
-     * while an iteration over the set is in progress (except through
-     * the iterator's own <tt>remove</tt> operation), the results of
-     * the iteration are undefined.  The set supports element removal,
-     * which removes the corresponding mapping from the map, via the
-     * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
-     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
-     * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
-     * operations.
+    /*
+     * The following package-protected methods are designed to be
+     * overridden by LinkedHashMap, but not by any other subclass.
+     * Nearly all other internal methods are also package-protected
+     * but are declared final, so can be used by LinkedHashMap, view
+     * classes, and HashSet.
      */
-    public Set<K> keySet() {
-        Set<K> ks = keySet;
-        return (ks != null ? ks : (keySet = new KeySet()));
+
+    // Create a regular (non-tree) node
+    Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
+        return new Node<>(hash, key, value, next);
     }
 
-    private final class KeySet extends AbstractSet<K> {
-        public Iterator<K> iterator() {
-            return newKeyIterator();
-        }
-        public int size() {
-            return size;
-        }
-        public boolean contains(Object o) {
-            return containsKey(o);
-        }
-        public boolean remove(Object o) {
-            return HashMap.this.removeEntryForKey(o) != null;
-        }
-        public void clear() {
-            HashMap.this.clear();
-        }
-        public final Spliterator<K> spliterator() {
-            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
-        }
-        public final void forEach(Consumer<? super K> action) {
-            HashMapEntry<K,V>[] tab;
-            if (action == null)
-                throw new NullPointerException();
-            if (size > 0 && (tab = table) != null) {
-                int mc = modCount;
-                for (int i = 0; i < tab.length; ++i) {
-                    for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
-                        action.accept(e.key);
-                        // Android-modified - this was outside of the loop, inconsistent with other
-                        // collections
-                        if (modCount != mc) {
-                            throw new ConcurrentModificationException();
-                        }
-                    }
-                }
+    // For conversion from TreeNodes to plain nodes
+    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
+        return new Node<>(p.hash, p.key, p.value, next);
+    }
 
-            }
-        }
+    // Create a tree bin node
+    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
+        return new TreeNode<>(hash, key, value, next);
+    }
+
+    // For treeifyBin
+    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
+        return new TreeNode<>(p.hash, p.key, p.value, next);
     }
 
     /**
-     * Returns a {@link Collection} view of the values contained in this map.
-     * The collection is backed by the map, so changes to the map are
-     * reflected in the collection, and vice-versa.  If the map is
-     * modified while an iteration over the collection is in progress
-     * (except through the iterator's own <tt>remove</tt> operation),
-     * the results of the iteration are undefined.  The collection
-     * supports element removal, which removes the corresponding
-     * mapping from the map, via the <tt>Iterator.remove</tt>,
-     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
-     * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
-     * support the <tt>add</tt> or <tt>addAll</tt> operations.
+     * Reset to initial default state.  Called by clone and readObject.
      */
-    public Collection<V> values() {
-        Collection<V> vs = values;
-        return (vs != null ? vs : (values = new Values()));
+    void reinitialize() {
+        table = null;
+        entrySet = null;
+        keySet = null;
+        values = null;
+        modCount = 0;
+        threshold = 0;
+        size = 0;
     }
 
-    private final class Values extends AbstractCollection<V> {
-        public Iterator<V> iterator() {
-            return newValueIterator();
-        }
-        public int size() {
-            return size;
-        }
-        public boolean contains(Object o) {
-            return containsValue(o);
-        }
-        public void clear() {
-            HashMap.this.clear();
-        }
-        public final Spliterator<V> spliterator() {
-            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
-        }
-        public final void forEach(Consumer<? super V> action) {
-            HashMapEntry<K,V>[] tab;
-            if (action == null)
-                throw new NullPointerException();
-            if (size > 0 && (tab = table) != null) {
-                int mc = modCount;
-                for (int i = 0; i < tab.length; ++i) {
-                    for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
-                        action.accept(e.value);
-                        // Android-modified - this was outside of the loop, inconsistent with other
-                        // collections
-                        if (modCount != mc) {
-                            throw new ConcurrentModificationException();
-                        }
-                    }
+    // Callbacks to allow LinkedHashMap post-actions
+    void afterNodeAccess(Node<K,V> p) { }
+    void afterNodeInsertion(boolean evict) { }
+    void afterNodeRemoval(Node<K,V> p) { }
+
+    // Called only from writeObject, to ensure compatible ordering.
+    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+        Node<K,V>[] tab;
+        if (size > 0 && (tab = table) != null) {
+            for (int i = 0; i < tab.length; ++i) {
+                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+                    s.writeObject(e.key);
+                    s.writeObject(e.value);
                 }
             }
         }
     }
 
+    /* ------------------------------------------------------------ */
+    // Tree bins
+
     /**
-     * Returns a {@link Set} view of the mappings contained in this map.
-     * The set is backed by the map, so changes to the map are
-     * reflected in the set, and vice-versa.  If the map is modified
-     * while an iteration over the set is in progress (except through
-     * the iterator's own <tt>remove</tt> operation, or through the
-     * <tt>setValue</tt> operation on a map entry returned by the
-     * iterator) the results of the iteration are undefined.  The set
-     * supports element removal, which removes the corresponding
-     * mapping from the map, via the <tt>Iterator.remove</tt>,
-     * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
-     * <tt>clear</tt> operations.  It does not support the
-     * <tt>add</tt> or <tt>addAll</tt> operations.
-     *
-     * @return a set view of the mappings contained in this map
+     * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
+     * extends Node) so can be used as extension of either regular or
+     * linked node.
      */
-    // Android-changed: Changed type parameter from <? extends Entry<K, V>
-    // to a Map.Entry<K, V>.
-    public Set<Map.Entry<K,V>> entrySet() {
-        return entrySet0();
-    }
-
-    private Set<Map.Entry<K,V>> entrySet0() {
-        Set<Map.Entry<K,V>> es = entrySet;
-        return es != null ? es : (entrySet = new EntrySet());
-    }
-
-    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
-        public Iterator<Map.Entry<K,V>> iterator() {
-            return newEntryIterator();
+    static final class TreeNode<K,V> extends LinkedHashMap.LinkedHashMapEntry<K,V> {
+        TreeNode<K,V> parent;  // red-black tree links
+        TreeNode<K,V> left;
+        TreeNode<K,V> right;
+        TreeNode<K,V> prev;    // needed to unlink next upon deletion
+        boolean red;
+        TreeNode(int hash, K key, V val, Node<K,V> next) {
+            super(hash, key, val, next);
         }
-        public boolean contains(Object o) {
-            if (!(o instanceof Map.Entry))
+
+        /**
+         * Returns root of tree containing this node.
+         */
+        final TreeNode<K,V> root() {
+            for (TreeNode<K,V> r = this, p;;) {
+                if ((p = r.parent) == null)
+                    return r;
+                r = p;
+            }
+        }
+
+        /**
+         * Ensures that the given root is the first node of its bin.
+         */
+        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
+            int n;
+            if (root != null && tab != null && (n = tab.length) > 0) {
+                int index = (n - 1) & root.hash;
+                TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
+                if (root != first) {
+                    Node<K,V> rn;
+                    tab[index] = root;
+                    TreeNode<K,V> rp = root.prev;
+                    if ((rn = root.next) != null)
+                        ((TreeNode<K,V>)rn).prev = rp;
+                    if (rp != null)
+                        rp.next = rn;
+                    if (first != null)
+                        first.prev = root;
+                    root.next = first;
+                    root.prev = null;
+                }
+                assert checkInvariants(root);
+            }
+        }
+
+        /**
+         * Finds the node starting at root p with the given hash and key.
+         * The kc argument caches comparableClassFor(key) upon first use
+         * comparing keys.
+         */
+        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
+            TreeNode<K,V> p = this;
+            do {
+                int ph, dir; K pk;
+                TreeNode<K,V> pl = p.left, pr = p.right, q;
+                if ((ph = p.hash) > h)
+                    p = pl;
+                else if (ph < h)
+                    p = pr;
+                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
+                    return p;
+                else if (pl == null)
+                    p = pr;
+                else if (pr == null)
+                    p = pl;
+                else if ((kc != null ||
+                          (kc = comparableClassFor(k)) != null) &&
+                         (dir = compareComparables(kc, k, pk)) != 0)
+                    p = (dir < 0) ? pl : pr;
+                else if ((q = pr.find(h, k, kc)) != null)
+                    return q;
+                else
+                    p = pl;
+            } while (p != null);
+            return null;
+        }
+
+        /**
+         * Calls find for root node.
+         */
+        final TreeNode<K,V> getTreeNode(int h, Object k) {
+            return ((parent != null) ? root() : this).find(h, k, null);
+        }
+
+        /**
+         * Tie-breaking utility for ordering insertions when equal
+         * hashCodes and non-comparable. We don't require a total
+         * order, just a consistent insertion rule to maintain
+         * equivalence across rebalancings. Tie-breaking further than
+         * necessary simplifies testing a bit.
+         */
+        static int tieBreakOrder(Object a, Object b) {
+            int d;
+            if (a == null || b == null ||
+                (d = a.getClass().getName().
+                 compareTo(b.getClass().getName())) == 0)
+                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
+                     -1 : 1);
+            return d;
+        }
+
+        /**
+         * Forms tree of the nodes linked from this node.
+         * @return root of tree
+         */
+        final void treeify(Node<K,V>[] tab) {
+            TreeNode<K,V> root = null;
+            for (TreeNode<K,V> x = this, next; x != null; x = next) {
+                next = (TreeNode<K,V>)x.next;
+                x.left = x.right = null;
+                if (root == null) {
+                    x.parent = null;
+                    x.red = false;
+                    root = x;
+                }
+                else {
+                    K k = x.key;
+                    int h = x.hash;
+                    Class<?> kc = null;
+                    for (TreeNode<K,V> p = root;;) {
+                        int dir, ph;
+                        K pk = p.key;
+                        if ((ph = p.hash) > h)
+                            dir = -1;
+                        else if (ph < h)
+                            dir = 1;
+                        else if ((kc == null &&
+                                  (kc = comparableClassFor(k)) == null) ||
+                                 (dir = compareComparables(kc, k, pk)) == 0)
+                            dir = tieBreakOrder(k, pk);
+
+                        TreeNode<K,V> xp = p;
+                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
+                            x.parent = xp;
+                            if (dir <= 0)
+                                xp.left = x;
+                            else
+                                xp.right = x;
+                            root = balanceInsertion(root, x);
+                            break;
+                        }
+                    }
+                }
+            }
+            moveRootToFront(tab, root);
+        }
+
+        /**
+         * Returns a list of non-TreeNodes replacing those linked from
+         * this node.
+         */
+        final Node<K,V> untreeify(HashMap<K,V> map) {
+            Node<K,V> hd = null, tl = null;
+            for (Node<K,V> q = this; q != null; q = q.next) {
+                Node<K,V> p = map.replacementNode(q, null);
+                if (tl == null)
+                    hd = p;
+                else
+                    tl.next = p;
+                tl = p;
+            }
+            return hd;
+        }
+
+        /**
+         * Tree version of putVal.
+         */
+        final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
+                                       int h, K k, V v) {
+            Class<?> kc = null;
+            boolean searched = false;
+            TreeNode<K,V> root = (parent != null) ? root() : this;
+            for (TreeNode<K,V> p = root;;) {
+                int dir, ph; K pk;
+                if ((ph = p.hash) > h)
+                    dir = -1;
+                else if (ph < h)
+                    dir = 1;
+                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
+                    return p;
+                else if ((kc == null &&
+                          (kc = comparableClassFor(k)) == null) ||
+                         (dir = compareComparables(kc, k, pk)) == 0) {
+                    if (!searched) {
+                        TreeNode<K,V> q, ch;
+                        searched = true;
+                        if (((ch = p.left) != null &&
+                             (q = ch.find(h, k, kc)) != null) ||
+                            ((ch = p.right) != null &&
+                             (q = ch.find(h, k, kc)) != null))
+                            return q;
+                    }
+                    dir = tieBreakOrder(k, pk);
+                }
+
+                TreeNode<K,V> xp = p;
+                if ((p = (dir <= 0) ? p.left : p.right) == null) {
+                    Node<K,V> xpn = xp.next;
+                    TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
+                    if (dir <= 0)
+                        xp.left = x;
+                    else
+                        xp.right = x;
+                    xp.next = x;
+                    x.parent = x.prev = xp;
+                    if (xpn != null)
+                        ((TreeNode<K,V>)xpn).prev = x;
+                    moveRootToFront(tab, balanceInsertion(root, x));
+                    return null;
+                }
+            }
+        }
+
+        /**
+         * Removes the given node, that must be present before this call.
+         * This is messier than typical red-black deletion code because we
+         * cannot swap the contents of an interior node with a leaf
+         * successor that is pinned by "next" pointers that are accessible
+         * independently during traversal. So instead we swap the tree
+         * linkages. If the current tree appears to have too few nodes,
+         * the bin is converted back to a plain bin. (The test triggers
+         * somewhere between 2 and 6 nodes, depending on tree structure).
+         */
+        final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
+                                  boolean movable) {
+            int n;
+            if (tab == null || (n = tab.length) == 0)
+                return;
+            int index = (n - 1) & hash;
+            TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
+            TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
+            if (pred == null)
+                tab[index] = first = succ;
+            else
+                pred.next = succ;
+            if (succ != null)
+                succ.prev = pred;
+            if (first == null)
+                return;
+            if (root.parent != null)
+                root = root.root();
+            if (root == null || root.right == null ||
+                (rl = root.left) == null || rl.left == null) {
+                tab[index] = first.untreeify(map);  // too small
+                return;
+            }
+            TreeNode<K,V> p = this, pl = left, pr = right, replacement;
+            if (pl != null && pr != null) {
+                TreeNode<K,V> s = pr, sl;
+                while ((sl = s.left) != null) // find successor
+                    s = sl;
+                boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+                TreeNode<K,V> sr = s.right;
+                TreeNode<K,V> pp = p.parent;
+                if (s == pr) { // p was s's direct parent
+                    p.parent = s;
+                    s.right = p;
+                }
+                else {
+                    TreeNode<K,V> sp = s.parent;
+                    if ((p.parent = sp) != null) {
+                        if (s == sp.left)
+                            sp.left = p;
+                        else
+                            sp.right = p;
+                    }
+                    if ((s.right = pr) != null)
+                        pr.parent = s;
+                }
+                p.left = null;
+                if ((p.right = sr) != null)
+                    sr.parent = p;
+                if ((s.left = pl) != null)
+                    pl.parent = s;
+                if ((s.parent = pp) == null)
+                    root = s;
+                else if (p == pp.left)
+                    pp.left = s;
+                else
+                    pp.right = s;
+                if (sr != null)
+                    replacement = sr;
+                else
+                    replacement = p;
+            }
+            else if (pl != null)
+                replacement = pl;
+            else if (pr != null)
+                replacement = pr;
+            else
+                replacement = p;
+            if (replacement != p) {
+                TreeNode<K,V> pp = replacement.parent = p.parent;
+                if (pp == null)
+                    root = replacement;
+                else if (p == pp.left)
+                    pp.left = replacement;
+                else
+                    pp.right = replacement;
+                p.left = p.right = p.parent = null;
+            }
+
+            TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
+
+            if (replacement == p) {  // detach
+                TreeNode<K,V> pp = p.parent;
+                p.parent = null;
+                if (pp != null) {
+                    if (p == pp.left)
+                        pp.left = null;
+                    else if (p == pp.right)
+                        pp.right = null;
+                }
+            }
+            if (movable)
+                moveRootToFront(tab, r);
+        }
+
+        /**
+         * Splits nodes in a tree bin into lower and upper tree bins,
+         * or untreeifies if now too small. Called only from resize;
+         * see above discussion about split bits and indices.
+         *
+         * @param map the map
+         * @param tab the table for recording bin heads
+         * @param index the index of the table being split
+         * @param bit the bit of hash to split on
+         */
+        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
+            TreeNode<K,V> b = this;
+            // Relink into lo and hi lists, preserving order
+            TreeNode<K,V> loHead = null, loTail = null;
+            TreeNode<K,V> hiHead = null, hiTail = null;
+            int lc = 0, hc = 0;
+            for (TreeNode<K,V> e = b, next; e != null; e = next) {
+                next = (TreeNode<K,V>)e.next;
+                e.next = null;
+                if ((e.hash & bit) == 0) {
+                    if ((e.prev = loTail) == null)
+                        loHead = e;
+                    else
+                        loTail.next = e;
+                    loTail = e;
+                    ++lc;
+                }
+                else {
+                    if ((e.prev = hiTail) == null)
+                        hiHead = e;
+                    else
+                        hiTail.next = e;
+                    hiTail = e;
+                    ++hc;
+                }
+            }
+
+            if (loHead != null) {
+                if (lc <= UNTREEIFY_THRESHOLD)
+                    tab[index] = loHead.untreeify(map);
+                else {
+                    tab[index] = loHead;
+                    if (hiHead != null) // (else is already treeified)
+                        loHead.treeify(tab);
+                }
+            }
+            if (hiHead != null) {
+                if (hc <= UNTREEIFY_THRESHOLD)
+                    tab[index + bit] = hiHead.untreeify(map);
+                else {
+                    tab[index + bit] = hiHead;
+                    if (loHead != null)
+                        hiHead.treeify(tab);
+                }
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        // Red-black tree methods, all adapted from CLR
+
+        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
+                                              TreeNode<K,V> p) {
+            TreeNode<K,V> r, pp, rl;
+            if (p != null && (r = p.right) != null) {
+                if ((rl = p.right = r.left) != null)
+                    rl.parent = p;
+                if ((pp = r.parent = p.parent) == null)
+                    (root = r).red = false;
+                else if (pp.left == p)
+                    pp.left = r;
+                else
+                    pp.right = r;
+                r.left = p;
+                p.parent = r;
+            }
+            return root;
+        }
+
+        static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
+                                               TreeNode<K,V> p) {
+            TreeNode<K,V> l, pp, lr;
+            if (p != null && (l = p.left) != null) {
+                if ((lr = p.left = l.right) != null)
+                    lr.parent = p;
+                if ((pp = l.parent = p.parent) == null)
+                    (root = l).red = false;
+                else if (pp.right == p)
+                    pp.right = l;
+                else
+                    pp.left = l;
+                l.right = p;
+                p.parent = l;
+            }
+            return root;
+        }
+
+        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
+                                                    TreeNode<K,V> x) {
+            x.red = true;
+            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
+                if ((xp = x.parent) == null) {
+                    x.red = false;
+                    return x;
+                }
+                else if (!xp.red || (xpp = xp.parent) == null)
+                    return root;
+                if (xp == (xppl = xpp.left)) {
+                    if ((xppr = xpp.right) != null && xppr.red) {
+                        xppr.red = false;
+                        xp.red = false;
+                        xpp.red = true;
+                        x = xpp;
+                    }
+                    else {
+                        if (x == xp.right) {
+                            root = rotateLeft(root, x = xp);
+                            xpp = (xp = x.parent) == null ? null : xp.parent;
+                        }
+                        if (xp != null) {
+                            xp.red = false;
+                            if (xpp != null) {
+                                xpp.red = true;
+                                root = rotateRight(root, xpp);
+                            }
+                        }
+                    }
+                }
+                else {
+                    if (xppl != null && xppl.red) {
+                        xppl.red = false;
+                        xp.red = false;
+                        xpp.red = true;
+                        x = xpp;
+                    }
+                    else {
+                        if (x == xp.left) {
+                            root = rotateRight(root, x = xp);
+                            xpp = (xp = x.parent) == null ? null : xp.parent;
+                        }
+                        if (xp != null) {
+                            xp.red = false;
+                            if (xpp != null) {
+                                xpp.red = true;
+                                root = rotateLeft(root, xpp);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
+                                                   TreeNode<K,V> x) {
+            for (TreeNode<K,V> xp, xpl, xpr;;)  {
+                if (x == null || x == root)
+                    return root;
+                else if ((xp = x.parent) == null) {
+                    x.red = false;
+                    return x;
+                }
+                else if (x.red) {
+                    x.red = false;
+                    return root;
+                }
+                else if ((xpl = xp.left) == x) {
+                    if ((xpr = xp.right) != null && xpr.red) {
+                        xpr.red = false;
+                        xp.red = true;
+                        root = rotateLeft(root, xp);
+                        xpr = (xp = x.parent) == null ? null : xp.right;
+                    }
+                    if (xpr == null)
+                        x = xp;
+                    else {
+                        TreeNode<K,V> sl = xpr.left, sr = xpr.right;
+                        if ((sr == null || !sr.red) &&
+                            (sl == null || !sl.red)) {
+                            xpr.red = true;
+                            x = xp;
+                        }
+                        else {
+                            if (sr == null || !sr.red) {
+                                if (sl != null)
+                                    sl.red = false;
+                                xpr.red = true;
+                                root = rotateRight(root, xpr);
+                                xpr = (xp = x.parent) == null ?
+                                    null : xp.right;
+                            }
+                            if (xpr != null) {
+                                xpr.red = (xp == null) ? false : xp.red;
+                                if ((sr = xpr.right) != null)
+                                    sr.red = false;
+                            }
+                            if (xp != null) {
+                                xp.red = false;
+                                root = rotateLeft(root, xp);
+                            }
+                            x = root;
+                        }
+                    }
+                }
+                else { // symmetric
+                    if (xpl != null && xpl.red) {
+                        xpl.red = false;
+                        xp.red = true;
+                        root = rotateRight(root, xp);
+                        xpl = (xp = x.parent) == null ? null : xp.left;
+                    }
+                    if (xpl == null)
+                        x = xp;
+                    else {
+                        TreeNode<K,V> sl = xpl.left, sr = xpl.right;
+                        if ((sl == null || !sl.red) &&
+                            (sr == null || !sr.red)) {
+                            xpl.red = true;
+                            x = xp;
+                        }
+                        else {
+                            if (sl == null || !sl.red) {
+                                if (sr != null)
+                                    sr.red = false;
+                                xpl.red = true;
+                                root = rotateLeft(root, xpl);
+                                xpl = (xp = x.parent) == null ?
+                                    null : xp.left;
+                            }
+                            if (xpl != null) {
+                                xpl.red = (xp == null) ? false : xp.red;
+                                if ((sl = xpl.left) != null)
+                                    sl.red = false;
+                            }
+                            if (xp != null) {
+                                xp.red = false;
+                                root = rotateRight(root, xp);
+                            }
+                            x = root;
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Recursive invariant check
+         */
+        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
+            TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
+                tb = t.prev, tn = (TreeNode<K,V>)t.next;
+            if (tb != null && tb.next != t)
                 return false;
-            Map.Entry<K,V> e = (Map.Entry<K,V>) o;
-            Entry<K,V> candidate = getEntry(e.getKey());
-            return candidate != null && candidate.equals(e);
-        }
-        public boolean remove(Object o) {
-            return removeMapping(o) != null;
-        }
-        public int size() {
-            return size;
-        }
-        public void clear() {
-            HashMap.this.clear();
-        }
-        public final Spliterator<Map.Entry<K,V>> spliterator() {
-            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
-        }
-        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
-            HashMapEntry<K,V>[] tab;
-            if (action == null)
-                throw new NullPointerException();
-            if (size > 0 && (tab = table) != null) {
-                int mc = modCount;
-                for (int i = 0; i < tab.length; ++i) {
-                    for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
-                        action.accept(e);
-                        // Android-modified - this was outside of the loop, inconsistent with other
-                        // collections
-                        if (modCount != mc) {
-                            throw new ConcurrentModificationException();
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void forEach(BiConsumer<? super K, ? super V> action) {
-        HashMapEntry<K,V>[] tab;
-        if (action == null)
-            throw new NullPointerException();
-        if (size > 0 && (tab = table) != null) {
-            int mc = modCount;
-            for (int i = 0; i < tab.length; ++i) {
-                for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
-                    action.accept(e.key, e.value);
-                    // Android-modified - this was outside of the loop, inconsistent with other
-                    // collections
-                    if (modCount != mc) {
-                        throw new ConcurrentModificationException();
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
-        HashMapEntry<K,V>[] tab;
-        if (function == null)
-            throw new NullPointerException();
-        if (size > 0 && (tab = table) != null) {
-            int mc = modCount;
-            for (int i = 0; i < tab.length; ++i) {
-                for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
-                    e.value = function.apply(e.key, e.value);
-                }
-            }
-            if (modCount != mc)
-                throw new ConcurrentModificationException();
-        }
-    }
-
-    /**
-     * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
-     * serialize it).
-     *
-     * @serialData The <i>capacity</i> of the HashMap (the length of the
-     *             bucket array) is emitted (int), followed by the
-     *             <i>size</i> (an int, the number of key-value
-     *             mappings), followed by the key (Object) and value (Object)
-     *             for each key-value mapping.  The key-value mappings are
-     *             emitted in no particular order.
-     */
-    private void writeObject(java.io.ObjectOutputStream s)
-        throws IOException
-    {
-        // Write out the threshold, loadfactor, and any hidden stuff
-        s.defaultWriteObject();
-
-        // Write out number of buckets
-        if (table==EMPTY_TABLE) {
-            s.writeInt(roundUpToPowerOf2(threshold));
-        } else {
-           s.writeInt(table.length);
-        }
-
-        // Write out size (number of Mappings)
-        s.writeInt(size);
-
-        // Write out keys and values (alternating)
-        if (size > 0) {
-            for(Map.Entry<K,V> e : entrySet0()) {
-                s.writeObject(e.getKey());
-                s.writeObject(e.getValue());
-            }
-        }
-    }
-
-    private static final long serialVersionUID = 362498820763181265L;
-
-    /**
-     * Reconstitute the {@code HashMap} instance from a stream (i.e.,
-     * deserialize it).
-     */
-    private void readObject(java.io.ObjectInputStream s)
-         throws IOException, ClassNotFoundException
-    {
-        // Read in the threshold (ignored), loadfactor, and any hidden stuff
-        s.defaultReadObject();
-        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
-            throw new InvalidObjectException("Illegal load factor: " +
-                                               loadFactor);
-        }
-
-        // set other fields that need values
-        table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
-
-        // Read in number of buckets
-        s.readInt(); // ignored.
-
-        // Read number of mappings
-        int mappings = s.readInt();
-        if (mappings < 0)
-            throw new InvalidObjectException("Illegal mappings count: " +
-                                               mappings);
-
-        // capacity chosen by number of mappings and desired load (if >= 0.25)
-        int capacity = (int) Math.min(
-                    mappings * Math.min(1 / loadFactor, 4.0f),
-                    // we have limits...
-                    HashMap.MAXIMUM_CAPACITY);
-
-        // allocate the bucket array;
-        if (mappings > 0) {
-            inflateTable(capacity);
-        } else {
-            threshold = capacity;
-        }
-
-        init();  // Give subclass a chance to do its thing.
-
-        // Read the keys and values, and put the mappings in the HashMap
-        for (int i = 0; i < mappings; i++) {
-            K key = (K) s.readObject();
-            V value = (V) s.readObject();
-            putForCreate(key, value);
-        }
-    }
-
-    @Override
-    public boolean replace(K key, V oldValue, V newValue) {
-        HashMapEntry<K,V> e; V v;
-        if ((e = (HashMapEntry)getEntry(key)) != null &&
-                ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
-            e.value = newValue;
-            e.recordAccess(this);
+            if (tn != null && tn.prev != t)
+                return false;
+            if (tp != null && t != tp.left && t != tp.right)
+                return false;
+            if (tl != null && (tl.parent != t || tl.hash > t.hash))
+                return false;
+            if (tr != null && (tr.parent != t || tr.hash < t.hash))
+                return false;
+            if (t.red && tl != null && tl.red && tr != null && tr.red)
+                return false;
+            if (tl != null && !checkInvariants(tl))
+                return false;
+            if (tr != null && !checkInvariants(tr))
+                return false;
             return true;
         }
-        return false;
     }
 
-    // These methods are used when serializing HashSets
-    int   capacity()     { return table.length; }
-    float loadFactor()   { return loadFactor;   }
 }
diff --git a/ojluni/src/main/java/java/util/HashSet.java b/ojluni/src/main/java/java/util/HashSet.java
index ab5fe4d..ebbe43c 100644
--- a/ojluni/src/main/java/java/util/HashSet.java
+++ b/ojluni/src/main/java/java/util/HashSet.java
@@ -25,6 +25,8 @@
 
 package java.util;
 
+import java.io.InvalidObjectException;
+
 /**
  * This class implements the <tt>Set</tt> interface, backed by a hash table
  * (actually a <tt>HashMap</tt> instance).  It makes no guarantees as to the
@@ -247,13 +249,14 @@
      *
      * @return a shallow copy of this set
      */
+    @SuppressWarnings("unchecked")
     public Object clone() {
         try {
             HashSet<E> newSet = (HashSet<E>) super.clone();
             newSet.map = (HashMap<E, Object>) map.clone();
             return newSet;
         } catch (CloneNotSupportedException e) {
-            throw new InternalError();
+            throw new InternalError(e);
         }
     }
 
@@ -293,19 +296,41 @@
         // Read in any hidden serialization magic
         s.defaultReadObject();
 
-        // Read in HashMap capacity and load factor and create backing HashMap
+        // Read capacity and verify non-negative.
         int capacity = s.readInt();
+        if (capacity < 0) {
+            throw new InvalidObjectException("Illegal capacity: " +
+                                             capacity);
+        }
+
+        // Read load factor and verify positive and non NaN.
         float loadFactor = s.readFloat();
-        map = (((HashSet)this) instanceof LinkedHashSet ?
+        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
+            throw new InvalidObjectException("Illegal load factor: " +
+                                             loadFactor);
+        }
+
+        // Read size and verify non-negative.
+        int size = s.readInt();
+        if (size < 0) {
+            throw new InvalidObjectException("Illegal size: " +
+                                             size);
+        }
+
+        // Set the capacity according to the size and load factor ensuring that
+        // the HashMap is at least 25% full but clamping to maximum capacity.
+        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
+                HashMap.MAXIMUM_CAPACITY);
+
+        // Create backing HashMap
+        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
                new LinkedHashMap<E,Object>(capacity, loadFactor) :
                new HashMap<E,Object>(capacity, loadFactor));
 
-        // Read in size
-        int size = s.readInt();
-
         // Read in all elements in the proper order.
         for (int i=0; i<size; i++) {
-            E e = (E) s.readObject();
+            @SuppressWarnings("unchecked")
+                E e = (E) s.readObject();
             map.put(e, PRESENT);
         }
     }
diff --git a/ojluni/src/main/java/java/util/LinkedHashMap.java b/ojluni/src/main/java/java/util/LinkedHashMap.java
index 0396e8c..dd32423 100644
--- a/ojluni/src/main/java/java/util/LinkedHashMap.java
+++ b/ojluni/src/main/java/java/util/LinkedHashMap.java
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -26,12 +25,10 @@
 
 package java.util;
 
-import sun.misc.Hashing;
-
-import java.io.*;
-import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.io.IOException;
 
 /**
  * <p>Hash table and linked list implementation of the <tt>Map</tt> interface,
@@ -75,7 +72,8 @@
  * key-value mappings are provided by the specified map's entry set iterator.
  * <i>No other methods generate entry accesses.</i>  In particular, operations
  * on collection-views do <i>not</i> affect the order of iteration of the
- * backing map. *
+ * backing map.
+ *
  * <p>The {@link #removeEldestEntry(Map.Entry)} method may be overridden to
  * impose a policy for removing stale mappings automatically when new mappings
  * are added to the map.
@@ -116,8 +114,8 @@
  * iteration order.  In insertion-ordered linked hash maps, merely changing
  * the value associated with a key that is already contained in the map is not
  * a structural modification.  <strong>In access-ordered linked hash maps,
- * merely querying the map with <tt>get</tt> is a structural
- * modification.</strong>)
+ * merely querying the map with <tt>get</tt> is a structural modification.
+ * </strong>)
  *
  * <p>The iterators returned by the <tt>iterator</tt> method of the collections
  * returned by all of this class's collection view methods are
@@ -142,7 +140,7 @@
  * <em>fail-fast</em>, and additionally report {@link Spliterator#ORDERED}.
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/collections/index.html">
+ * <a href="{@docRoot}/../technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @implNote
@@ -162,18 +160,59 @@
  * @see     Hashtable
  * @since   1.4
  */
-
 public class LinkedHashMap<K,V>
     extends HashMap<K,V>
     implements Map<K,V>
 {
 
+    /*
+     * Implementation note.  A previous version of this class was
+     * internally structured a little differently. Because superclass
+     * HashMap now uses trees for some of its nodes, class
+     * LinkedHashMap.Entry is now treated as intermediary node class
+     * that can also be converted to tree form.
+     *
+     * Android-changed BEGIN
+     * LinkedHashMapEntry should not be renamed. Specifically, for
+     * source compatibility with earlier versions of Android, this
+     * nested class must not be named "Entry". Otherwise, it would
+     * hide Map.Entry which would break compilation of code like:
+     *
+     * LinkedHashMap.Entry<K, V> entry = map.entrySet().iterator.next()
+     *
+     * To compile, that code snippet's "LinkedHashMap.Entry" must
+     * mean java.util.Map.Entry which is the compile time type of
+     * entrySet()'s elements.
+     * Android-changed END
+     *
+     * The changes in node classes also require using two fields
+     * (head, tail) rather than a pointer to a header node to maintain
+     * the doubly-linked before/after list. This class also
+     * previously used a different style of callback methods upon
+     * access, insertion, and removal.
+     */
+
+    /**
+     * HashMap.Node subclass for normal LinkedHashMap entries.
+     */
+    static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
+        LinkedHashMapEntry<K,V> before, after;
+        LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
+            super(hash, key, value, next);
+        }
+    }
+
     private static final long serialVersionUID = 3801124242820219131L;
 
     /**
-     * The head of the doubly linked list.
+     * The head (eldest) of the doubly linked list.
      */
-    private transient LinkedHashMapEntry<K,V> header;
+    transient LinkedHashMapEntry<K,V> head;
+
+    /**
+     * The tail (youngest) of the doubly linked list.
+     */
+    transient LinkedHashMapEntry<K,V> tail;
 
     /**
      * The iteration ordering method for this linked hash map: <tt>true</tt>
@@ -181,7 +220,125 @@
      *
      * @serial
      */
-    private final boolean accessOrder;
+    final boolean accessOrder;
+
+    // internal utilities
+
+    // link at the end of list
+    private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
+        LinkedHashMapEntry<K,V> last = tail;
+        tail = p;
+        if (last == null)
+            head = p;
+        else {
+            p.before = last;
+            last.after = p;
+        }
+    }
+
+    // apply src's links to dst
+    private void transferLinks(LinkedHashMapEntry<K,V> src,
+                               LinkedHashMapEntry<K,V> dst) {
+        LinkedHashMapEntry<K,V> b = dst.before = src.before;
+        LinkedHashMapEntry<K,V> a = dst.after = src.after;
+        if (b == null)
+            head = dst;
+        else
+            b.after = dst;
+        if (a == null)
+            tail = dst;
+        else
+            a.before = dst;
+    }
+
+    // overrides of HashMap hook methods
+
+    void reinitialize() {
+        super.reinitialize();
+        head = tail = null;
+    }
+
+    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
+        LinkedHashMapEntry<K,V> p =
+            new LinkedHashMapEntry<K,V>(hash, key, value, e);
+        linkNodeLast(p);
+        return p;
+    }
+
+    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
+        LinkedHashMapEntry<K,V> q = (LinkedHashMapEntry<K,V>)p;
+        LinkedHashMapEntry<K,V> t =
+            new LinkedHashMapEntry<K,V>(q.hash, q.key, q.value, next);
+        transferLinks(q, t);
+        return t;
+    }
+
+    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
+        TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
+        linkNodeLast(p);
+        return p;
+    }
+
+    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
+        LinkedHashMapEntry<K,V> q = (LinkedHashMapEntry<K,V>)p;
+        TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
+        transferLinks(q, t);
+        return t;
+    }
+
+    void afterNodeRemoval(Node<K,V> e) { // unlink
+        LinkedHashMapEntry<K,V> p =
+            (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
+        p.before = p.after = null;
+        if (b == null)
+            head = a;
+        else
+            b.after = a;
+        if (a == null)
+            tail = b;
+        else
+            a.before = b;
+    }
+
+    void afterNodeInsertion(boolean evict) { // possibly remove eldest
+        LinkedHashMapEntry<K,V> first;
+        if (evict && (first = head) != null && removeEldestEntry(first)) {
+            K key = first.key;
+            removeNode(hash(key), key, null, false, true);
+        }
+    }
+
+    void afterNodeAccess(Node<K,V> e) { // move node to last
+        LinkedHashMapEntry<K,V> last;
+        if (accessOrder && (last = tail) != e) {
+            LinkedHashMapEntry<K,V> p =
+                (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
+            p.after = null;
+            if (b == null)
+                head = a;
+            else
+                b.after = a;
+            if (a != null)
+                a.before = b;
+            else
+                last = b;
+            if (last == null)
+                head = p;
+            else {
+                p.before = last;
+                last.after = p;
+            }
+            tail = p;
+            ++modCount;
+        }
+    }
+
+    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+        for (LinkedHashMapEntry<K,V> e = head; e != null; e = e.after) {
+            s.writeObject(e.key);
+            s.writeObject(e.value);
+        }
+    }
 
     /**
      * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
@@ -228,8 +385,9 @@
      * @throws NullPointerException if the specified map is null
      */
     public LinkedHashMap(Map<? extends K, ? extends V> m) {
-        super(m);
+        super();
         accessOrder = false;
+        putMapEntries(m, false);
     }
 
     /**
@@ -250,32 +408,6 @@
         this.accessOrder = accessOrder;
     }
 
-    /**
-     * Called by superclass constructors and pseudoconstructors (clone,
-     * readObject) before any entries are inserted into the map.  Initializes
-     * the chain.
-     */
-    @Override
-    void init() {
-        header = new LinkedHashMapEntry<>(-1, null, null, null);
-        header.before = header.after = header;
-    }
-
-    /**
-     * Transfers all entries to new table array.  This method is called
-     * by superclass resize.  It is overridden for performance, as it is
-     * faster to iterate using our linked list.
-     */
-    @Override
-    void transfer(HashMapEntry[] newTable) {
-        int newCapacity = newTable.length;
-        for (LinkedHashMapEntry<K,V> e = header.after; e != header; e = e.after) {
-            int index = indexFor(e.hash, newCapacity);
-            e.next = newTable[index];
-            newTable[index] = e;
-        }
-    }
-
 
     /**
      * Returns <tt>true</tt> if this map maps one or more keys to the
@@ -286,15 +418,10 @@
      *         specified value
      */
     public boolean containsValue(Object value) {
-        // Overridden to take advantage of faster iterator
-        if (value==null) {
-            for (LinkedHashMapEntry e = header.after; e != header; e = e.after)
-                if (e.value==null)
-                    return true;
-        } else {
-            for (LinkedHashMapEntry e = header.after; e != header; e = e.after)
-                if (value.equals(e.value))
-                    return true;
+        for (LinkedHashMapEntry<K,V> e = head; e != null; e = e.after) {
+            V v = e.value;
+            if (v == value || (value != null && value.equals(v)))
+                return true;
         }
         return false;
     }
@@ -315,153 +442,32 @@
      * distinguish these two cases.
      */
     public V get(Object key) {
-        LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
-        if (e == null)
+        Node<K,V> e;
+        if ((e = getNode(hash(key), key)) == null)
             return null;
-        e.recordAccess(this);
+        if (accessOrder)
+            afterNodeAccess(e);
         return e.value;
     }
 
     /**
-     * Removes all of the mappings from this map.
-     * The map will be empty after this call returns.
+     * {@inheritDoc}
+     */
+    public V getOrDefault(Object key, V defaultValue) {
+       Node<K,V> e;
+       if ((e = getNode(hash(key), key)) == null)
+           return defaultValue;
+       if (accessOrder)
+           afterNodeAccess(e);
+       return e.value;
+   }
+
+    /**
+     * {@inheritDoc}
      */
     public void clear() {
         super.clear();
-        header.before = header.after = header;
-    }
-
-    /**
-     * LinkedHashMap entry.
-     */
-    private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V> {
-        // These fields comprise the doubly linked list used for iteration.
-        LinkedHashMapEntry<K,V> before, after;
-
-        LinkedHashMapEntry(int hash, K key, V value, HashMapEntry<K,V> next) {
-            super(hash, key, value, next);
-        }
-
-        /**
-         * Removes this entry from the linked list.
-         */
-        private void remove() {
-            before.after = after;
-            after.before = before;
-        }
-
-        /**
-         * Inserts this entry before the specified existing entry in the list.
-         */
-        private void addBefore(LinkedHashMapEntry<K,V> existingEntry) {
-            after  = existingEntry;
-            before = existingEntry.before;
-            before.after = this;
-            after.before = this;
-        }
-
-        /**
-         * This method is invoked by the superclass whenever the value
-         * of a pre-existing entry is read by Map.get or modified by Map.set.
-         * If the enclosing Map is access-ordered, it moves the entry
-         * to the end of the list; otherwise, it does nothing.
-         */
-        void recordAccess(HashMap<K,V> m) {
-            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
-            if (lm.accessOrder) {
-                lm.modCount++;
-                remove();
-                addBefore(lm.header);
-            }
-        }
-
-        void recordRemoval(HashMap<K,V> m) {
-            remove();
-        }
-    }
-
-    private abstract class LinkedHashIterator<T> implements Iterator<T> {
-        LinkedHashMapEntry<K,V> nextEntry    = header.after;
-        LinkedHashMapEntry<K,V> lastReturned = null;
-
-        /**
-         * The modCount value that the iterator believes that the backing
-         * List should have.  If this expectation is violated, the iterator
-         * has detected concurrent modification.
-         */
-        int expectedModCount = modCount;
-
-        public boolean hasNext() {
-            return nextEntry != header;
-        }
-
-        public void remove() {
-            if (lastReturned == null)
-                throw new IllegalStateException();
-            if (modCount != expectedModCount)
-                throw new ConcurrentModificationException();
-
-            LinkedHashMap.this.remove(lastReturned.key);
-            lastReturned = null;
-            expectedModCount = modCount;
-        }
-
-        Entry<K,V> nextEntry() {
-            if (modCount != expectedModCount)
-                throw new ConcurrentModificationException();
-            if (nextEntry == header)
-                throw new NoSuchElementException();
-
-            LinkedHashMapEntry<K,V> e = lastReturned = nextEntry;
-            nextEntry = e.after;
-            return e;
-        }
-    }
-
-    private class KeyIterator extends LinkedHashIterator<K> {
-        public K next() { return nextEntry().getKey(); }
-    }
-
-    private class ValueIterator extends LinkedHashIterator<V> {
-        public V next() { return nextEntry().getValue(); }
-    }
-
-    private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
-        public Map.Entry<K,V> next() { return nextEntry(); }
-    }
-
-    // These Overrides alter the behavior of superclass view iterator() methods
-    Iterator<K> newKeyIterator()   { return new KeyIterator();   }
-    Iterator<V> newValueIterator() { return new ValueIterator(); }
-    Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
-
-    /**
-     * This override alters behavior of superclass put method. It causes newly
-     * allocated entry to get inserted at the end of the linked list and
-     * removes the eldest entry if appropriate.
-     */
-    void addEntry(int hash, K key, V value, int bucketIndex) {
-        // Previous Android releases called removeEldestEntry() before actually
-        // inserting a value but after increasing the size.
-        // The RI is documented to call it afterwards.
-        // **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
-
-        // Remove eldest entry if instructed
-        LinkedHashMapEntry<K,V> eldest = header.after;
-        if (eldest != header) {
-            boolean removeEldest;
-            size++;
-            try {
-                removeEldest = removeEldestEntry(eldest);
-            } finally {
-                size--;
-            }
-            if (removeEldest) {
-                removeEntryForKey(eldest.key);
-            }
-        }
-
-        super.addEntry(hash, key, value, bucketIndex);
+        head = tail = null;
     }
 
     /**
@@ -472,28 +478,10 @@
      * @hide
      */
     public Map.Entry<K, V> eldest() {
-        Entry<K, V> eldest = header.after;
-        return eldest != header ? eldest : null;
+        return head;
     }
 
     /**
-     * This override differs from addEntry in that it doesn't resize the
-     * table or remove the eldest entry.
-     */
-    void createEntry(int hash, K key, V value, int bucketIndex) {
-        HashMapEntry<K,V> old = table[bucketIndex];
-        LinkedHashMapEntry<K,V> e = new LinkedHashMapEntry<>(hash, key, value, old);
-        table[bucketIndex] = e;
-        e.addBefore(header);
-        size++;
-    }
-
-    // Intentionally make this not JavaDoc, as the we don't conform to
-    // the behaviour documented here (we call removeEldestEntry before
-    // inserting the new value to be consistent with previous Android
-    // releases).
-    // **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
-    /*
      * Returns <tt>true</tt> if this map should remove its eldest entry.
      * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
      * inserting a new entry into the map.  It provides the implementor
@@ -508,7 +496,7 @@
      *     private static final int MAX_ENTRIES = 100;
      *
      *     protected boolean removeEldestEntry(Map.Entry eldest) {
-     *        return size() > MAX_ENTRIES;
+     *        return size() &gt; MAX_ENTRIES;
      *     }
      * </pre>
      *
@@ -538,13 +526,174 @@
         return false;
     }
 
+    /**
+     * Returns a {@link Set} view of the keys contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own <tt>remove</tt> operation), the results of
+     * the iteration are undefined.  The set supports element removal,
+     * which removes the corresponding mapping from the map, via the
+     * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+     * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
+     * operations.
+     * Its {@link Spliterator} typically provides faster sequential
+     * performance but much poorer parallel performance than that of
+     * {@code HashMap}.
+     *
+     * @return a set view of the keys contained in this map
+     */
+    public Set<K> keySet() {
+        Set<K> ks;
+        return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
+    }
+
+    final class LinkedKeySet extends AbstractSet<K> {
+        public final int size()                 { return size; }
+        public final void clear()               { LinkedHashMap.this.clear(); }
+        public final Iterator<K> iterator() {
+            return new LinkedKeyIterator();
+        }
+        public final boolean contains(Object o) { return containsKey(o); }
+        public final boolean remove(Object key) {
+            return removeNode(hash(key), key, null, false, true) != null;
+        }
+        public final Spliterator<K> spliterator()  {
+            return Spliterators.spliterator(this, Spliterator.SIZED |
+                                            Spliterator.ORDERED |
+                                            Spliterator.DISTINCT);
+        }
+        public final void forEach(Consumer<? super K> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int mc = modCount;
+            // Android-changed: Detect changes to modCount early.
+            for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after)
+                action.accept(e.key);
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this map.
+     * The collection is backed by the map, so changes to the map are
+     * reflected in the collection, and vice-versa.  If the map is
+     * modified while an iteration over the collection is in progress
+     * (except through the iterator's own <tt>remove</tt> operation),
+     * the results of the iteration are undefined.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the <tt>Iterator.remove</tt>,
+     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
+     * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
+     * support the <tt>add</tt> or <tt>addAll</tt> operations.
+     * Its {@link Spliterator} typically provides faster sequential
+     * performance but much poorer parallel performance than that of
+     * {@code HashMap}.
+     *
+     * @return a view of the values contained in this map
+     */
+    public Collection<V> values() {
+        Collection<V> vs;
+        return (vs = values) == null ? (values = new LinkedValues()) : vs;
+    }
+
+    final class LinkedValues extends AbstractCollection<V> {
+        public final int size()                 { return size; }
+        public final void clear()               { LinkedHashMap.this.clear(); }
+        public final Iterator<V> iterator() {
+            return new LinkedValueIterator();
+        }
+        public final boolean contains(Object o) { return containsValue(o); }
+        public final Spliterator<V> spliterator() {
+            return Spliterators.spliterator(this, Spliterator.SIZED |
+                                            Spliterator.ORDERED);
+        }
+        public final void forEach(Consumer<? super V> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int mc = modCount;
+            // Android-changed: Detect changes to modCount early.
+            for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after)
+                action.accept(e.value);
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * Returns a {@link Set} view of the mappings contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own <tt>remove</tt> operation, or through the
+     * <tt>setValue</tt> operation on a map entry returned by the
+     * iterator) the results of the iteration are undefined.  The set
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the <tt>Iterator.remove</tt>,
+     * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
+     * <tt>clear</tt> operations.  It does not support the
+     * <tt>add</tt> or <tt>addAll</tt> operations.
+     * Its {@link Spliterator} typically provides faster sequential
+     * performance but much poorer parallel performance than that of
+     * {@code HashMap}.
+     *
+     * @return a set view of the mappings contained in this map
+     */
+    public Set<Map.Entry<K,V>> entrySet() {
+        Set<Map.Entry<K,V>> es;
+        return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
+    }
+
+    final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
+        public final int size()                 { return size; }
+        public final void clear()               { LinkedHashMap.this.clear(); }
+        public final Iterator<Map.Entry<K,V>> iterator() {
+            return new LinkedEntryIterator();
+        }
+        public final boolean contains(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+            Object key = e.getKey();
+            Node<K,V> candidate = getNode(hash(key), key);
+            return candidate != null && candidate.equals(e);
+        }
+        public final boolean remove(Object o) {
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+                Object key = e.getKey();
+                Object value = e.getValue();
+                return removeNode(hash(key), key, value, true, true) != null;
+            }
+            return false;
+        }
+        public final Spliterator<Map.Entry<K,V>> spliterator() {
+            return Spliterators.spliterator(this, Spliterator.SIZED |
+                                            Spliterator.ORDERED |
+                                            Spliterator.DISTINCT);
+        }
+        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int mc = modCount;
+            // Android-changed: Detect changes to modCount early.
+            for (LinkedHashMapEntry<K,V> e = head; (e != null && mc == modCount); e = e.after)
+                action.accept(e);
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
     // Map overrides
+
     public void forEach(BiConsumer<? super K, ? super V> action) {
         if (action == null)
             throw new NullPointerException();
         int mc = modCount;
-        // Android modified - breaks from the loop when modCount != mc
-        for (LinkedHashMapEntry<K,V> e = header.after; modCount == mc && e != header; e = e.after)
+        // Android-changed: Detect changes to modCount early.
+        for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after)
             action.accept(e.key, e.value);
         if (modCount != mc)
             throw new ConcurrentModificationException();
@@ -554,10 +703,68 @@
         if (function == null)
             throw new NullPointerException();
         int mc = modCount;
-        // Android modified - breaks from the loop when modCount != mc
-        for (LinkedHashMapEntry<K,V> e = header.after; modCount == mc && e != header; e = e.after)
+        // Android-changed: Detect changes to modCount early.
+        for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after)
             e.value = function.apply(e.key, e.value);
         if (modCount != mc)
             throw new ConcurrentModificationException();
     }
+
+    // Iterators
+
+    abstract class LinkedHashIterator {
+        LinkedHashMapEntry<K,V> next;
+        LinkedHashMapEntry<K,V> current;
+        int expectedModCount;
+
+        LinkedHashIterator() {
+            next = head;
+            expectedModCount = modCount;
+            current = null;
+        }
+
+        public final boolean hasNext() {
+            return next != null;
+        }
+
+        final LinkedHashMapEntry<K,V> nextNode() {
+            LinkedHashMapEntry<K,V> e = next;
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            if (e == null)
+                throw new NoSuchElementException();
+            current = e;
+            next = e.after;
+            return e;
+        }
+
+        public final void remove() {
+            Node<K,V> p = current;
+            if (p == null)
+                throw new IllegalStateException();
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            current = null;
+            K key = p.key;
+            removeNode(hash(key), key, null, false, false);
+            expectedModCount = modCount;
+        }
+    }
+
+    final class LinkedKeyIterator extends LinkedHashIterator
+        implements Iterator<K> {
+        public final K next() { return nextNode().getKey(); }
+    }
+
+    final class LinkedValueIterator extends LinkedHashIterator
+        implements Iterator<V> {
+        public final V next() { return nextNode().value; }
+    }
+
+    final class LinkedEntryIterator extends LinkedHashIterator
+        implements Iterator<Map.Entry<K,V>> {
+        public final Map.Entry<K,V> next() { return nextNode(); }
+    }
+
+
 }
diff --git a/ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java b/ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java
deleted file mode 100644
index b833dc0..0000000
--- a/ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (c) 2003, 2006, 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 sun.reflect.annotation;
-
-import java.lang.annotation.*;
-import java.lang.reflect.*;
-import java.util.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
-/**
- * Represents an annotation type at run time.  Used to type-check annotations
- * and apply member defaults.
- *
- * @author  Josh Bloch
- * @since   1.5
- */
-public class AnnotationType {
-    /**
-     * Member name -> type mapping. Note that primitive types
-     * are represented by the class objects for the corresponding wrapper
-     * types.  This matches the return value that must be used for a
-     * dynamic proxy, allowing for a simple isInstance test.
-     */
-    private final Map<String, Class<?>> memberTypes = new HashMap<String,Class<?>>();
-
-    /**
-     * Member name -> default value mapping.
-     */
-    private final Map<String, Object> memberDefaults =
-        new HashMap<String, Object>();
-
-    /**
-     * Member name -> Method object mapping. This (and its assoicated
-     * accessor) are used only to generate AnnotationTypeMismatchExceptions.
-     */
-    private final Map<String, Method> members = new HashMap<String, Method>();
-
-    /**
-     * The retention policy for this annotation type.
-     */
-    private RetentionPolicy retention = RetentionPolicy.RUNTIME;;
-
-    /**
-     * Whether this annotation type is inherited.
-     */
-    private boolean inherited = false;
-
-    /**
-     * Returns an AnnotationType instance for the specified annotation type.
-     *
-     * @throw IllegalArgumentException if the specified class object for
-     *     does not represent a valid annotation type
-     */
-    public static synchronized AnnotationType getInstance(
-        Class<? extends Annotation> annotationClass)
-    {
-        AnnotationType result = annotationClass.getAnnotationType();
-        if (result == null)
-            result = new AnnotationType((Class<? extends Annotation>) annotationClass);
-
-        return result;
-    }
-
-    /**
-     * Sole constructor.
-     *
-     * @param annotationClass the class object for the annotation type
-     * @throw IllegalArgumentException if the specified class object for
-     *     does not represent a valid annotation type
-     */
-    private AnnotationType(final Class<? extends Annotation> annotationClass) {
-        if (!annotationClass.isAnnotation())
-            throw new IllegalArgumentException("Not an annotation type");
-
-        Method[] methods =
-            AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
-                public Method[] run() {
-                    // Initialize memberTypes and defaultValues
-                    return annotationClass.getDeclaredMethods();
-                }
-            });
-
-
-        for (Method method :  methods) {
-            if (method.getParameterTypes().length != 0)
-                throw new IllegalArgumentException(method + " has params");
-            String name = method.getName();
-            Class<?> type = method.getReturnType();
-            memberTypes.put(name, invocationHandlerReturnType(type));
-            members.put(name, method);
-
-            Object defaultValue = method.getDefaultValue();
-            if (defaultValue != null)
-                memberDefaults.put(name, defaultValue);
-
-            members.put(name, method);
-        }
-
-        annotationClass.setAnnotationType(this);
-
-        // Initialize retention, & inherited fields.  Special treatment
-        // of the corresponding annotation types breaks infinite recursion.
-        if (annotationClass != Retention.class &&
-            annotationClass != Inherited.class) {
-            Retention ret = annotationClass.getAnnotation(Retention.class);
-            retention = (ret == null ? RetentionPolicy.CLASS : ret.value());
-            inherited = annotationClass.isAnnotationPresent(Inherited.class);
-        }
-    }
-
-    /**
-     * Returns the type that must be returned by the invocation handler
-     * of a dynamic proxy in order to have the dynamic proxy return
-     * the specified type (which is assumed to be a legal member type
-     * for an annotation).
-     */
-    public static Class<?> invocationHandlerReturnType(Class<?> type) {
-        // Translate primitives to wrappers
-        if (type == byte.class)
-            return Byte.class;
-        if (type == char.class)
-            return Character.class;
-        if (type == double.class)
-            return Double.class;
-        if (type == float.class)
-            return Float.class;
-        if (type == int.class)
-            return Integer.class;
-        if (type == long.class)
-            return Long.class;
-        if (type == short.class)
-            return Short.class;
-        if (type == boolean.class)
-            return Boolean.class;
-
-        // Otherwise, just return declared type
-        return type;
-    }
-
-    /**
-     * Returns member types for this annotation type
-     * (member name -> type mapping).
-     */
-    public Map<String, Class<?>> memberTypes() {
-        return memberTypes;
-    }
-
-    /**
-     * Returns members of this annotation type
-     * (member name -> associated Method object mapping).
-     */
-    public Map<String, Method> members() {
-        return members;
-    }
-
-    /**
-     * Returns the default values for this annotation type
-     * (Member name -> default value mapping).
-     */
-    public Map<String, Object> memberDefaults() {
-        return memberDefaults;
-    }
-
-    /**
-     * Returns the retention policy for this annotation type.
-     */
-    public RetentionPolicy retention() {
-        return retention;
-    }
-
-    /**
-     * Returns true if this this annotation type is inherited.
-     */
-    public boolean isInherited() {
-        return inherited;
-    }
-
-    /**
-     * For debugging.
-     */
-    public String toString() {
-        StringBuffer s = new StringBuffer("Annotation Type:" + "\n");
-        s.append("   Member types: " + memberTypes + "\n");
-        s.append("   Member defaults: " + memberDefaults + "\n");
-        s.append("   Retention policy: " + retention + "\n");
-        s.append("   Inherited: " + inherited);
-        return s.toString();
-    }
-}
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index 145e344..fe4ad2d 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -1157,6 +1157,7 @@
     ojluni/src/main/java/sun/misc/FloatingDecimal.java \
     ojluni/src/main/java/java/lang/invoke/LambdaConversionException.java \
     ojluni/src/main/java/java/lang/invoke/MethodHandle.java \
+    ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java \
     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 \
@@ -1414,7 +1415,6 @@
     ojluni/src/main/java/sun/nio/fs/UnixUriUtils.java \
     ojluni/src/main/java/sun/nio/fs/UnixUserPrincipals.java \
     ojluni/src/main/java/sun/nio/fs/Util.java \
-    ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java \
     ojluni/src/main/java/sun/reflect/ConstructorAccessor.java \
     ojluni/src/main/java/sun/reflect/misc/ReflectUtil.java \
     ojluni/src/main/java/sun/reflect/Reflection.java \
@@ -1618,7 +1618,6 @@
 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/MethodHandleInfo.java \
     ojluni/src/lambda/java/java/lang/invoke/MethodHandles.java \
     ojluni/src/lambda/java/java/lang/invoke/SerializedLambda.java
 openjdk_lambda_duplicate_stub_files := \