Add list of allowable experimental notations

Upstream CL aosp/2081700 surfaced up allowed experimental annotations.

Bug: 222554358
Test: presubmit
Change-Id: I2199b68f56c91f35191a9f8172b2e6a198e58daa
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
index b484ffc..3a09d59 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
@@ -197,14 +197,24 @@
         val isUsedInSameArtifact = usageCoordinates.artifactId == annotationCoordinates.artifactId
         val isAtomic = atomicGroupList.contains(usageGroupId)
 
+        val annotationQualifiedName = annotation.getQualifiedName()
+        val isAnnotationAllowed = if (annotationQualifiedName != null) {
+            isAnnotationAlwaysAllowed(annotationQualifiedName)
+        } else {
+            false
+        }
+
         /**
          * Usage of experimental APIs is allowed in either of the following conditions:
          *
          * - Both the group ID and artifact ID in `usageCoordinates` and
          *   `annotationCoordinates` match
          * - The group IDs match, and that group ID is atomic
+         * - The annotation being used is is an allowlist
          */
-        if ((isUsedInSameGroup && isUsedInSameArtifact) || (isUsedInSameGroup && isAtomic)) return
+        if ((isUsedInSameGroup && isUsedInSameArtifact) ||
+            (isUsedInSameGroup && isAtomic) ||
+            isAnnotationAllowed) return
 
         // Log inappropriate experimental usage
         if (DEBUG) {
@@ -233,7 +243,12 @@
                 return coord
             }
         }
-        return getLibrary(File(findJarPath(element)))
+        val findJarPath = findJarPath(element)
+        return if (findJarPath != null) {
+            getLibrary(File(findJarPath))
+        } else {
+            null
+        }
     }
 
     private fun UElement.getQualifiedName() = (this as UClass).qualifiedName
@@ -282,5 +297,19 @@
                 Scope.JAVA_FILE_SCOPE,
             ),
         )
+
+        /**
+         * Checks to see if the given annotation is always allowed for use in @OptIn.
+         * For now, allow Kotlin all stdlib experimental annotations.
+         */
+        internal fun isAnnotationAlwaysAllowed(annotation: String): Boolean {
+            val allowedExperimentalAnnotations = listOf(
+                Regex("kotlin\\..*"),
+                Regex("kotlinx\\..*"),
+            )
+            return allowedExperimentalAnnotations.any {
+                annotation.matches(it)
+            }
+        }
     }
 }
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
index 5512fd3..129087f 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
@@ -18,6 +18,7 @@
 
 package androidx.build.lint
 
+import androidx.build.lint.BanInappropriateExperimentalUsage.Companion.isAnnotationAlwaysAllowed
 import com.android.tools.lint.checks.infrastructure.ProjectDescription
 import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.checks.infrastructure.TestMode
@@ -44,6 +45,20 @@
 ) {
 
     @Test
+    fun `Check if annotation is always allowed`() {
+
+        // List of Kotlin stdlib experimental annotations used in AndroidX
+        assertTrue(isAnnotationAlwaysAllowed("kotlin.contracts.ExperimentalContracts"))
+        assertTrue(isAnnotationAlwaysAllowed("kotlin.ExperimentalStdlibApi"))
+        assertTrue(isAnnotationAlwaysAllowed("kotlin.experimental.ExperimentalTypeInference"))
+        assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.DelicateCoroutinesApi"))
+        assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.ExperimentalCoroutinesApi"))
+
+        assertFalse(isAnnotationAlwaysAllowed("androidx.foo.bar"))
+        assertFalse(isAnnotationAlwaysAllowed("com.google.foo.bar"))
+    }
+
+    @Test
     fun `Test same atomic module Experimental usage via Gradle model`() {
         val provider = project()
             .name("provider")