Introduce the CovariantReturnType annotation

This commit introduces the CovariantReturnType annotation
that can be used to prepare the Android platform for future
API changes associated with more specific return types on
subclass methods.

The annotation introduces platform bytecode for the new
form of the method as a synthetic overload of the original,
public version.

This commit contains a first example:

A change to ConcurrentHashMap made in OpenJDK 8. Android
cannot make this change without breaking apps that compile
against latest stubs but have to run on older releases. The
API change can only be made once apps will no longer be run
on devices without the synthetic version (without app tooling
changes to accelerate the switch).

This commit also introduces a test to confirm that the platform
bytecode is present in the old and new forms.

There are platform tooling changes associated with this commit.

Bug: 70661641
Test: CTS: run cts -m CtsLibcoreTestCases
Change-Id: I39c4152223aed34642169689cba39de313358655
diff --git a/dalvik/src/main/java/dalvik/annotation/codegen/CovariantReturnType.java b/dalvik/src/main/java/dalvik/annotation/codegen/CovariantReturnType.java
new file mode 100644
index 0000000..f302850
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/codegen/CovariantReturnType.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.codegen;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates to the platform toolchain that there is an upcoming public SDK API change for a method.
+ * The API change will add a more specific return type in a subclass for an overridden method.
+ *
+ * <p>When this annotation is present, the toolchain is required to generate on-device bytecode for
+ * an overloaded implementation of the method which is marked as synthetic, returns the more
+ * specific type given in {@link CovariantReturnType#returnType()}, and which delegates to the
+ * method being annotated.
+ *
+ * <p>At some future point in time the public Android API can change so that the public API method
+ * returns the new type. Android platform developers can be sure that all releases after the API
+ * version specified in {@link CovariantReturnType#presentAfter()} have the new method signature on
+ * device and will therefore be compatible with code compiled against the newest public Android API
+ * stubs.
+ *
+ * <p>Once the actual method signature is updated to the new form and this annotation is removed
+ * there will be a synthetic overload for the method with the more general type provided (as normal)
+ * by the Java compiler ensuring compatibility with code compiled against the old stubs.
+ *
+ * <p>When adding this annotation to platform classes the developer should also add CTS tests to
+ * ensure the expected methods are present on all Android devices.
+ *
+ * <p>Notes on scope and limitations:
+ * <ul>
+ * <li>This annotation must have no effect on generated API stubs.</li>
+ * <li>This annotation does not allow the declared exceptions to be made more specific for the
+ * generated synthetic method. This could be added later.</li>
+ * <li>Any type parameters on the annotated method need not be copied.</li>
+ * <li>This annotation is <em>not</em> expected to be treated by the toolchain as inherited. All
+ * layers of platform class hierarchy must specify {@link CovariantReturnType} for all the overloads
+ * that have to be generated. The annotation is marked as repeatable for this reason.</li>
+ * </ul>
+ *
+ * @hide
+ */
+@Repeatable(CovariantReturnType.CovariantReturnTypes.class)
+@Retention(RetentionPolicy.CLASS)
+@Target({ ElementType.METHOD})
+public @interface CovariantReturnType {
+
+    /**
+     * The return type of the synthetic method to generate. Must be a subclass of the return type
+     * of the method being annotated.
+     */
+    Class<?> returnType();
+
+    /**
+     * The last Android API level not to have the generated synthetic method. The annotation can be
+     * removed and the actual return type updated when support for this API level is dropped.
+     */
+    int presentAfter();
+
+    /** @hide */
+    @Retention(RetentionPolicy.CLASS)
+    @Target({ElementType.METHOD})
+    @interface CovariantReturnTypes {
+        CovariantReturnType[] value();
+    }
+}
diff --git a/luni/src/test/java/libcore/libcore/internal/ApiEvolutionTest.java b/luni/src/test/java/libcore/libcore/internal/ApiEvolutionTest.java
new file mode 100644
index 0000000..49f8e2e
--- /dev/null
+++ b/luni/src/test/java/libcore/libcore/internal/ApiEvolutionTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 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.libcore.internal;
+
+import org.junit.Test;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import dalvik.annotation.codegen.CovariantReturnType;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * A test to ensure that platform bytecode is as expected to enable API evolution.
+ */
+public class ApiEvolutionTest {
+
+    /** Tests for synthetic methods generated by {@link CovariantReturnType}. */
+    @Test
+    public void testCovariantReturnTypeMethods() throws Exception {
+        // Example of the "normal" way round for synthetic methods generated by the toolchain when
+        // a subclass has a more specific return type.
+        // Exceptions are not required to be identical in this case because the synthetic method
+        // mirrors the superclass version.
+        assertSyntheticMethodOverloadExists(
+                Sub.class, "myMethod", new Class[] { Integer.class },
+                String.class, Object.class,
+                false /* requireIdenticalExceptions */);
+
+        // Cases of synthetic platform methods that have been introduced by the platform build tools
+        // in response to the presence of a @CovarientReturnType annotation.
+        // More should be added for every use of the annotation.
+        assertSyntheticMethodOverloadExists(ConcurrentHashMap.class, "keySet", null, Set.class,
+                ConcurrentHashMap.KeySetView.class, true);
+    }
+
+    private static void assertSyntheticMethodOverloadExists(
+            Class<?> clazz, String methodName, Class[] parameterTypes,
+            Class<?> originalReturnType, Class<?> syntheticReturnType,
+            boolean requireIdenticalExceptions) throws Exception {
+
+        if (parameterTypes == null) {
+            parameterTypes = new Class[0];
+        }
+        String fullMethodName = clazz + "." + methodName;
+
+        // Assert we find the original, non-synthetic version using getDeclaredMethod().
+        Method declaredMethod = clazz.getDeclaredMethod(methodName, parameterTypes);
+        assertEquals(originalReturnType, declaredMethod.getReturnType());
+
+        // Assert both versions of the method are returned from getDeclaredMethods().
+        Method original = null;
+        Method synthetic = null;
+        for (Method method : clazz.getDeclaredMethods()) {
+            if (methodMatches(methodName, parameterTypes, method)) {
+                if (method.getReturnType().equals(syntheticReturnType)) {
+                    synthetic = method;
+                } else if (method.getReturnType().equals(originalReturnType)) {
+                    original = method;
+                }
+            }
+        }
+        assertNotNull("Unable to find original signature: " + fullMethodName
+                + ", returning " + originalReturnType, original);
+        assertNotNull("Unable to find synthetic signature: " + fullMethodName
+                + ", returning " + syntheticReturnType, synthetic);
+
+        // Check modifiers are as expected.
+        assertFalse(original.isSynthetic());
+        assertFalse(original.isBridge());
+        assertTrue(synthetic.isSynthetic());
+        assertTrue(synthetic.isBridge());
+
+        int originalModifiers = original.getModifiers();
+        int syntheticModifiers = synthetic.getModifiers();
+
+        // Modifier.BRIDGE is not public but it's the same bit as VOLATILE.
+        int mask = Modifier.VOLATILE | Modifier.SYNTHETIC;
+        assertEquals("Method modifiers for " + fullMethodName
+                        + " are expected to be identical except for SYNTHETIC and BRIDGE."
+                        + " original=" + Modifier.toString(originalModifiers)
+                        + ", synthetic=" + Modifier.toString(syntheticModifiers),
+                originalModifiers | mask,
+                syntheticModifiers | mask);
+
+        // Exceptions are not required at method resolution time but we check they're the same in
+        // most cases for completeness.
+        if (requireIdenticalExceptions) {
+            assertArrayEquals("Exceptions for " + fullMethodName + " must be compatible",
+                    original.getExceptionTypes(), synthetic.getExceptionTypes());
+        }
+
+        // Android doesn't support runtime type annotations so nothing to do for them.
+
+        // Type parameters are *not* copied because they're not needed at method resolution time.
+        assertEquals(0, synthetic.getTypeParameters().length);
+
+        // Check method annotations.
+        Annotation[] annotations = original.getDeclaredAnnotations();
+        assertArrayEquals("Annotations differ between original and synthetic versions of "
+                + fullMethodName, annotations, synthetic.getDeclaredAnnotations());
+        Annotation[][] parameterAnnotations = original.getParameterAnnotations();
+        // Check parameter annotations.
+        assertArrayEquals("Annotations differ between original and synthetic versions of "
+                + fullMethodName, parameterAnnotations, synthetic.getParameterAnnotations());
+    }
+
+    private static boolean methodMatches(String methodName, Class[] parameterTypes, Method method) {
+        return method.getName().equals(methodName)
+                && Arrays.equals(parameterTypes, method.getParameterTypes());
+    }
+
+    /** Annotation used in return type specialization tests. */
+    @Retention(RetentionPolicy.RUNTIME)
+    private @interface TestAnnotation {}
+
+    /** Base class for return type specialization tests. */
+    private static class Base {
+        protected Object myMethod(Integer p1) throws Exception {
+            return null;
+        }
+    }
+
+    /** Sub class for return type specialization tests. */
+    private static class Sub extends Base {
+        @TestAnnotation
+        @Override
+        protected String myMethod(@TestAnnotation Integer p1) throws ParseException {
+            return null;
+        }
+    }
+}
diff --git a/non_openjdk_java_files.bp b/non_openjdk_java_files.bp
index 0dcca40..ca76b5e 100644
--- a/non_openjdk_java_files.bp
+++ b/non_openjdk_java_files.bp
@@ -38,6 +38,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/codegen/CovariantReturnType.java",
         "dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java",
         "dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java",
         "dalvik/src/main/java/dalvik/annotation/optimization/ReachabilitySensitive.java",
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
index 67c8622..5407963 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
@@ -1242,6 +1242,7 @@
      * @return the set view
      */
     // Android-changed: Return type for backwards compat. Was KeySetView<K,V>. http://b/28099367
+    @dalvik.annotation.codegen.CovariantReturnType(returnType = KeySetView.class, presentAfter = 28)
     public Set<K> keySet() {
         KeySetView<K,V> ks;
         return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));