111619374: Metalava stubs are missing @FunctionalInterface

Fixes: 111619374
Test: Unit test included
Change-Id: I446ad2d867609b0619c1c8f49420de9a4ea94416
Merged-In: I91edc5ca6ac8b318a7234543bca00f9cb876b1a1
diff --git a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
index 533abca..a3d9647 100644
--- a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
@@ -24,7 +24,6 @@
 import com.android.tools.lint.annotations.Extractor.ANDROID_INT_DEF
 import com.android.tools.lint.annotations.Extractor.ANDROID_LONG_DEF
 import com.android.tools.lint.annotations.Extractor.ANDROID_STRING_DEF
-import com.android.tools.lint.detector.api.startsWith
 import com.android.tools.metalava.ANDROIDX_ANNOTATION_PREFIX
 import com.android.tools.metalava.ANDROID_SUPPORT_ANNOTATION_PREFIX
 import com.android.tools.metalava.JAVA_LANG_PREFIX
@@ -152,8 +151,24 @@
                 return true
             }
 
-            return qualifiedName.startsWith("java.lang.annotation") &&
-                !qualifiedName.endsWith("SuppressWarnings")
+            // These are the significant annotations that should be included in the stubs.
+            // This is a hardcoded list here to minimize risk in the P release branch;
+            // in master the check is more general (we keep only runtime retention annotations
+            // that match the API filter, plus the retention one)
+            return when (qualifiedName) {
+                "android.view.ViewDebug.ExportedProperty",
+                "android.widget.RemoteViews.RemoteView",
+                "android.view.ViewDebug.CapturedViewProperty",
+
+                "java.lang.FunctionalInterface",
+                "java.lang.SafeVarargs",
+                "java.lang.annotation.Documented",
+                "java.lang.annotation.Inherited",
+                "java.lang.annotation.Repeatable",
+                "java.lang.annotation.Retention",
+                "java.lang.annotation.Target" -> true
+                else -> false
+            }
         }
 
         /** The simple name of an annotation, which is the annotation name (not qualified name) prefixed by @ */
@@ -425,8 +440,14 @@
             qualifiedName ?: return false
 
             return when (qualifiedName) {
+                // Hardcoded list for now; in master, this is generalized
+                "android.view.ViewDebug.ExportedProperty",
+                "android.widget.RemoteViews.RemoteView",
+                "android.view.ViewDebug.CapturedViewProperty",
+
                 "androidx.annotation.RecentlyNullable",
-                "androidx.annotation.RecentlyNonNull" -> true
+                "androidx.annotation.RecentlyNonNull" -> return true
+
                 else -> qualifiedName.startsWith("java.") ||
                     qualifiedName.startsWith("javax.") ||
                     qualifiedName.startsWith("kotlin.") ||
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt
index 4d78346..1aaca8e 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt
@@ -51,14 +51,20 @@
     override fun toString(): String = toSource()
 
     override fun toSource(): String {
-        val qualifiedName = qualifiedName() ?: return ""
+        val sb = StringBuilder(60)
+        appendAnnotation(sb, psiAnnotation)
+        return sb.toString()
+    }
+
+    private fun appendAnnotation(sb: StringBuilder, psiAnnotation: PsiAnnotation) {
+        val qualifiedName = AnnotationItem.mapName(codebase, psiAnnotation.qualifiedName) ?: return
 
         val attributes = psiAnnotation.parameterList.attributes
         if (attributes.isEmpty()) {
-            return "@$qualifiedName"
+            sb.append("@$qualifiedName")
+            return
         }
 
-        val sb = StringBuilder(30)
         sb.append("@")
         sb.append(qualifiedName)
         sb.append("(")
@@ -79,8 +85,6 @@
             }
         }
         sb.append(")")
-
-        return sb.toString()
     }
 
     override fun resolve(): ClassItem? {
@@ -150,6 +154,9 @@
                 }
                 sb.append('}')
             }
+            is PsiAnnotation -> {
+                appendAnnotation(sb, value)
+            }
             else -> {
                 if (value is PsiExpression) {
                     val source = getConstantSource(value)
diff --git a/src/test/java/com/android/tools/metalava/StubsTest.kt b/src/test/java/com/android/tools/metalava/StubsTest.kt
index 19ef97d..1edcbbf 100644
--- a/src/test/java/com/android/tools/metalava/StubsTest.kt
+++ b/src/test/java/com/android/tools/metalava/StubsTest.kt
@@ -3196,6 +3196,35 @@
     }
 
     @Test
+    fun `Functional Interfaces`() {
+        checkStubs(
+            compatibilityMode = false,
+            skipEmitPackages = emptyList(),
+            sourceFiles =
+            *arrayOf(
+                java(
+                    """
+                    package java.lang;
+
+                    @SuppressWarnings("something") @FunctionalInterface
+                    public interface MyInterface {
+                        void run();
+                    }
+                    """
+                )
+            ),
+            warnings = "",
+            source = """
+                package java.lang;
+                @SuppressWarnings({"unchecked", "deprecation", "all"})
+                @java.lang.FunctionalInterface public interface MyInterface {
+                public void run();
+                }
+                """
+        )
+    }
+
+    @Test
     fun `Check writing package info file`() {
         checkStubs(
             sourceFiles =