Add @Experimental annotation and Lint check

Bug: 135553341
Test: ExperimentalChecksTest
Change-Id: I25317eeeaf4e65fac6dcd485c87218f3b08037b1
diff --git a/annotation/annotation-experimental-lint/build.gradle b/annotation/annotation-experimental-lint/build.gradle
new file mode 100644
index 0000000..5d9a4a13
--- /dev/null
+++ b/annotation/annotation-experimental-lint/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.CompilationTarget
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+sourceSets {
+    // Make sample code and annotations available within JAR resources.
+    test.resources.srcDirs(
+            project(":annotation:annotation-experimental-lint-integration-tests").projectDir.toString() + "/src/main/java",
+            project(":annotation:annotation-experimental").projectDir.toString() + "/src/main/java"
+    )
+}
+
+dependencies {
+    compileOnly LINT_API_LATEST
+    compileOnly KOTLIN_STDLIB
+
+    testImplementation KOTLIN_STDLIB
+    testImplementation LINT_CORE
+    testImplementation LINT_TESTS
+}
+
+androidx {
+    name = "Experimental annotation lint checks"
+    toolingProject = true
+    publish = Publish.NONE
+    mavenVersion = LibraryVersions.ANNOTATION
+    mavenGroup = LibraryGroups.ANNOTATION
+    inceptionYear = "2019"
+    description = "Lint checks for the experimental annotation library"
+    url = ARCHITECTURE_URL
+    compilationTarget = CompilationTarget.HOST
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/build.gradle b/annotation/annotation-experimental-lint/integration-tests/build.gradle
new file mode 100644
index 0000000..434e274
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/build.gradle
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 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.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+dependencies {
+    implementation(project(":annotation"))
+    implementation(project(":annotation:annotation-experimental"))
+}
+
+androidx {
+    name = "Integration test for Experimental"
+    publish = Publish.NONE
+    mavenVersion = LibraryVersions.ANNOTATION
+    mavenGroup = LibraryGroups.ANNOTATION
+    inceptionYear = "2019"
+    description = "Suite of integration tests for the Experimental annotation"
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml b/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml
new file mode 100644
index 0000000..2fe55b4
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 3.5.0-beta04" client="gradle" variant="debug" version="3.5.0-beta04">
+
+    <issue
+        id="UnsafeExperimentalUsageError"
+        message="This declaration is experimental and its usage should be marked with&#xA;&apos;@sample.ExperimentalDateTime&apos; or &apos;@UseExperimental(sample.ExperimentalDateTime.class)&apos;"
+        errorLine1="        DateProvider provider = new DateProvider();"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/sample/UseExperimentalClassUnchecked.java"
+            line="29"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="UnsafeExperimentalUsageError"
+        message="This declaration is experimental and its usage should be marked with&#xA;&apos;@sample.ExperimentalDateTime&apos; or &apos;@UseExperimental(sample.ExperimentalDateTime.class)&apos;"
+        errorLine1="        return provider.getDate();"
+        errorLine2="                        ~~~~~~~">
+        <location
+            file="src/main/java/sample/UseExperimentalClassUnchecked.java"
+            line="30"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="UnsafeExperimentalUsageError"
+        message="This declaration is experimental and its usage should be marked with&#xA;&apos;@sample.ExperimentalDateTime&apos; or &apos;@UseExperimental(sample.ExperimentalDateTime.class)&apos;"
+        errorLine1="        System.out.println(getDate());"
+        errorLine2="                           ~~~~~~~">
+        <location
+            file="src/main/java/sample/UseExperimentalMethodUnchecked.java"
+            line="35"
+            column="28"/>
+    </issue>
+
+</issues>
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/AndroidManifest.xml b/annotation/annotation-experimental-lint/integration-tests/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ed18b0e
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 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.
+-->
+<manifest package="androidx.annotation.experimental.lint.integrationtests" />
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTime.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTime.java
new file mode 100644
index 0000000..e5d7b5da
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTime.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 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 sample;
+
+import static androidx.annotation.Experimental.Level.ERROR;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import androidx.annotation.Experimental;
+
+import java.lang.annotation.Retention;
+
+@Retention(CLASS)
+@Experimental(level = ERROR)
+@interface ExperimentalDateTime {}
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassChecked.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassChecked.java
new file mode 100644
index 0000000..981acf3
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassChecked.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 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 sample;
+
+@SuppressWarnings("unused")
+class UseExperimentalClassChecked {
+    @ExperimentalDateTime
+    class DateProvider {
+        int getDate() {
+            return -1;
+        }
+    }
+
+    @ExperimentalDateTime
+    int getDate() {
+        DateProvider provider = new DateProvider();
+        return provider.getDate();
+    }
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassCheckedUses.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassCheckedUses.java
new file mode 100644
index 0000000..b192512
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassCheckedUses.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 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 sample;
+
+import androidx.annotation.UseExperimental;
+
+@SuppressWarnings("unused")
+class UseExperimentalClassCheckedUses {
+    @ExperimentalDateTime
+    class DateProvider {
+        int getDate() {
+            return -1;
+        }
+    }
+
+    @UseExperimental(markerClass = ExperimentalDateTime.class)
+    int getDate() {
+        DateProvider provider = new DateProvider();
+        return provider.getDate();
+    }
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassUnchecked.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassUnchecked.java
new file mode 100644
index 0000000..758e2f3
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalClassUnchecked.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 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 sample;
+
+@SuppressWarnings("unused")
+class UseExperimentalClassUnchecked {
+    @ExperimentalDateTime
+    class DateProvider {
+        int getDate() {
+            return -1;
+        }
+    }
+
+    int getDate() {
+        DateProvider provider = new DateProvider();
+        return provider.getDate();
+    }
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalMethodUnchecked.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalMethodUnchecked.java
new file mode 100644
index 0000000..6ca57f7
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseExperimentalMethodUnchecked.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 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 sample;
+
+@SuppressWarnings("unused")
+class UseExperimentalMethodUnchecked {
+    @ExperimentalDateTime
+    class DateProvider {
+        int getDate() {
+            return -1;
+        }
+    }
+
+    @ExperimentalDateTime
+    int getDate() {
+        DateProvider provider = new DateProvider();
+        return provider.getDate();
+    }
+
+    void displayDate() {
+        System.out.println(getDate());
+    }
+}
diff --git a/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/lint/ExperimentalDetector.kt b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/lint/ExperimentalDetector.kt
new file mode 100644
index 0000000..16ae8f6
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/lint/ExperimentalDetector.kt
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2019 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 androidx.annotation.lint
+
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiClassType
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UAnnotated
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UNamedExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.getParentOfType
+
+class ExperimentalDetector : Detector(), SourceCodeScanner {
+    override fun applicableAnnotations(): List<String>? = listOf(
+        EXPERIMENTAL_ANNOTATION
+    )
+
+    override fun inheritAnnotation(annotation: String): Boolean = true
+
+    override fun visitAnnotationUsage(
+        context: JavaContext,
+        usage: UElement,
+        type: AnnotationUsageType,
+        annotation: UAnnotation,
+        qualifiedName: String,
+        method: PsiMethod?,
+        referenced: PsiElement?,
+        annotations: List<UAnnotation>,
+        allMemberAnnotations: List<UAnnotation>,
+        allClassAnnotations: List<UAnnotation>,
+        allPackageAnnotations: List<UAnnotation>
+    ) {
+        when (qualifiedName) {
+            EXPERIMENTAL_ANNOTATION -> {
+                checkExperimentalUsage(context, annotation, usage)
+            }
+        }
+    }
+
+    /**
+     * Check whether the given experimental API [annotation] can be referenced from [usage] call
+     * site.
+     *
+     * @param context the lint scanning context
+     * @param annotation the experimental annotation detected on the referenced element
+     * @param usage the element whose usage should be checked
+     */
+    private fun checkExperimentalUsage(
+        context: JavaContext,
+        annotation: UAnnotation,
+        usage: UElement
+    ) {
+        val useAnnotation = (annotation.uastParent as? UClass)?.qualifiedName ?: return
+        if (!hasOrUsesAnnotation(context, usage, useAnnotation)) {
+            val level = annotation.attributeValues[0].simpleName
+                ?: throw IllegalStateException("Failed to extract level from annotation")
+            report(context, usage, """
+                This declaration is experimental and its usage should be marked with
+                '@$useAnnotation' or '@UseExperimental($useAnnotation.class)'
+                """, level)
+        }
+    }
+
+    private fun hasOrUsesAnnotation(
+        context: JavaContext,
+        usage: UElement,
+        annotationName: String
+    ): Boolean {
+        var element: UAnnotated? = if (usage is UAnnotated) {
+            usage
+        } else {
+            usage.getParentOfType(UAnnotated::class.java)
+        }
+
+        while (element != null) {
+            val annotations = context.evaluator.getAllAnnotations(element, false)
+            val matchName = annotations.any { it.qualifiedName == annotationName }
+            val matchUse = annotations
+                .filter { annotation -> annotation.qualifiedName == USE_EXPERIMENTAL_ANNOTATION }
+                .mapNotNull { annotation -> annotation.attributeValues.getOrNull(0) }
+                .any { attrValue -> attrValue.getFullyQualifiedName(context) == annotationName }
+            if (matchName || matchUse) return true
+            element = element.getParentOfType(UAnnotated::class.java)
+        }
+        return false
+    }
+
+    /**
+     * Reports an issue and trims indentation on the [message].
+     */
+    private fun report(
+        context: JavaContext,
+        usage: UElement,
+        message: String,
+        level: String
+
+    ) {
+        val issue = when (level) {
+            "ERROR" -> ISSUE_ERROR
+            "WARNING" -> ISSUE_WARNING
+            else -> throw IllegalArgumentException("Level must be one of ERROR, WARNING")
+        }
+        context.report(issue, usage, context.getNameLocation(usage), message.trimIndent())
+    }
+
+    companion object {
+        private val IMPLEMENTATION = Implementation(
+            ExperimentalDetector::class.java,
+            Scope.JAVA_FILE_SCOPE
+        )
+
+        private const val EXPERIMENTAL_ANNOTATION = "androidx.annotation.Experimental"
+        private const val USE_EXPERIMENTAL_ANNOTATION = "androidx.annotation.UseExperimental"
+
+        private fun issueForLevel(level: String, severity: Severity): Issue = Issue.create(
+            id = "UnsafeExperimentalUsage${level.capitalize()}",
+            briefDescription = "Unsafe experimental usage intended to be $level-level severity",
+            explanation = """
+                This API has been flagged as experimental with $level-level severity.
+
+                Any declaration annotated with this marker is considered part of an unstable API \
+                surface and its call sites should accept the experimental aspect of it either by \
+                using `@UseExperimental`, or by being annotated with that marker themselves, \
+                effectively causing further propagation of that experimental aspect.
+            """,
+            category = Category.CORRECTNESS,
+            priority = 4,
+            severity = severity,
+            implementation = IMPLEMENTATION
+        )
+
+        val ISSUE_ERROR = issueForLevel("error", Severity.ERROR)
+        val ISSUE_WARNING = issueForLevel("warning", Severity.WARNING)
+        val ISSUES = listOf(ISSUE_ERROR, ISSUE_WARNING)
+    }
+}
+
+/**
+ * Returns the simple name (not qualified) associated with an expression, if any.
+ */
+private val UNamedExpression?.simpleName: String? get() = this?.let {
+    (expression as? USimpleNameReferenceExpression)?.identifier
+}
+
+/**
+ * Returns the fully-qualified class name for a given attribute value, if any.
+ */
+private fun UNamedExpression?.getFullyQualifiedName(context: JavaContext): String? =
+    this?.let { (evaluate() as? PsiClassType)?.let { context.evaluator.getQualifiedName(it) } }
diff --git a/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/lint/ExperimentalIssueRegistry.kt b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/lint/ExperimentalIssueRegistry.kt
new file mode 100644
index 0000000..7c311b6
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/lint/ExperimentalIssueRegistry.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 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 androidx.annotation.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+
+class ExperimentalIssueRegistry : IssueRegistry() {
+    override val issues get() = ExperimentalDetector.ISSUES
+}
diff --git a/annotation/annotation-experimental-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/annotation/annotation-experimental-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
new file mode 100644
index 0000000..636ae52
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
@@ -0,0 +1 @@
+androidx.annotation.lint.ExperimentalIssueRegistry
diff --git a/annotation/annotation-experimental-lint/src/test/kotlin/androidx/annotation/lint/ExperimentalDetectorTest.kt b/annotation/annotation-experimental-lint/src/test/kotlin/androidx/annotation/lint/ExperimentalDetectorTest.kt
new file mode 100644
index 0000000..2a00f5e
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/test/kotlin/androidx/annotation/lint/ExperimentalDetectorTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019 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 androidx.annotation.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles.java
+import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ExperimentalDetectorTest {
+
+    private fun checkJava(vararg testFiles: TestFile): TestLintResult {
+        return lint()
+            .files(
+                javaSample("androidx.annotation.Experimental"),
+                javaSample("androidx.annotation.UseExperimental"),
+                *testFiles
+            )
+            .allowMissingSdk(true)
+            .issues(*ExperimentalDetector.ISSUES.toTypedArray())
+            .run()
+    }
+
+    /**
+     * Loads a [TestFile] from Java source code included in the JAR resources.
+     */
+    private fun javaSample(className: String): TestFile {
+        return java(javaClass.getResource("/${className.replace('.','/')}.java").readText())
+    }
+
+    @Test
+    fun useExperimentalClassUnchecked() {
+        val input = arrayOf(
+            javaSample("sample.ExperimentalDateTime"),
+            javaSample("sample.UseExperimentalClassUnchecked")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/UseExperimentalClassUnchecked.java:29: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+        DateProvider provider = new DateProvider();
+                                ~~~~~~~~~~~~~~~~~~
+src/sample/UseExperimentalClassUnchecked.java:30: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+        return provider.getDate();
+                        ~~~~~~~
+2 errors, 0 warnings
+    """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        checkJava(*input).expect(expected)
+    }
+
+    @Test
+    fun useExperimentalClassChecked() {
+        val input = arrayOf(
+            javaSample("sample.ExperimentalDateTime"),
+            javaSample("sample.UseExperimentalClassChecked")
+        )
+
+        val expected = "No warnings."
+
+        checkJava(*input).expect(expected)
+    }
+
+    @Test
+    fun useExperimentalMethodUnchecked() {
+        val input = arrayOf(
+            javaSample("sample.ExperimentalDateTime"),
+            javaSample("sample.UseExperimentalMethodUnchecked")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/UseExperimentalMethodUnchecked.java:35: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+        System.out.println(getDate());
+                           ~~~~~~~
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        checkJava(*input).expect(expected)
+    }
+
+    @Test
+    fun useExperimentalClassCheckedUses() {
+        val input = arrayOf(
+            javaSample("sample.ExperimentalDateTime"),
+            javaSample("sample.UseExperimentalClassCheckedUses")
+        )
+
+        val expected = "No warnings."
+
+        checkJava(*input).expect(expected)
+    }
+}
diff --git a/annotation/annotation-experimental/build.gradle b/annotation/annotation-experimental/build.gradle
new file mode 100644
index 0000000..922edae
--- /dev/null
+++ b/annotation/annotation-experimental/build.gradle
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 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.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+dependencies {
+    lintPublish(project(":annotation:annotation-experimental-lint"))
+}
+
+androidx {
+    name = "Experimental annotation"
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenVersion = LibraryVersions.ANNOTATION
+    mavenGroup = LibraryGroups.ANNOTATION
+    inceptionYear = "2019"
+    description = "Annotation for use on unstable API surfaces"
+}
diff --git a/annotation/annotation-experimental/src/main/AndroidManifest.xml b/annotation/annotation-experimental/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0294c29
--- /dev/null
+++ b/annotation/annotation-experimental/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 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.
+-->
+<manifest package="androidx.annotation.experimental" />
diff --git a/annotation/annotation-experimental/src/main/java/androidx/annotation/Experimental.java b/annotation/annotation-experimental/src/main/java/androidx/annotation/Experimental.java
new file mode 100644
index 0000000..7986afe3
--- /dev/null
+++ b/annotation/annotation-experimental/src/main/java/androidx/annotation/Experimental.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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 androidx.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element is a marker of an experimental API.
+ *
+ * Any declaration annotated with this marker is considered part of an unstable API surface and its
+ * call sites should accept the experimental aspect of it either by using {@link UseExperimental},
+ * or by being annotated with that marker themselves, effectively causing further propagation of
+ * that experimental aspect.
+ *
+ * Example:
+ * <pre><code>
+ * // Library code
+ * &#64;Retention(CLASS)
+ * &#64;Target({TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE})
+ * &#64;Experimental(level = Level.ERROR)
+ * public @interface ExperimentalDateTime {}
+ *
+ * &#64;ExperimentalDateTime
+ * public class DateProvider {
+ *     // ...
+ * }
+ * </code></pre>
+ *
+ * <pre><code>
+ * // Client code
+ * int getYear() {
+ *     DateProvider provider; // Error: DateProvider is experimental
+ *     // ...
+ * }
+ *
+ * &#64;ExperimentalDateTime
+ * Date getDate() {
+ *     DateProvider provider; // OK: the function is marked as experimental
+ *     // ...
+ * }
+ *
+ * void displayDate() {
+ *     System.out.println(getDate()); // Error: getDate() is experimental, acceptance is required
+ * }
+ * </code></pre>
+ *
+ */
+@Retention(CLASS)
+@Target({ANNOTATION_TYPE})
+public @interface Experimental {
+    /**
+     * Severity of the diagnostic that should be reported on usages of experimental API which did
+     * not explicitly accept the experimental aspect of that API either by using
+     * {@link UseExperimental} or by being annotated with the corresponding marker annotation.
+     */
+    enum Level {
+        /**
+         * Specifies that a warning should be reported on incorrect usages of this experimental API.
+         */
+        WARNING,
+
+        /**
+         * Specifies that an error should be reported on incorrect usages of this experimental API.
+         */
+        ERROR,
+    }
+
+    /**
+     * Defines the reporting level for incorrect usages of this experimental API.
+     */
+    Level level() default Level.ERROR;
+}
diff --git a/annotation/annotation-experimental/src/main/java/androidx/annotation/UseExperimental.java b/annotation/annotation-experimental/src/main/java/androidx/annotation/UseExperimental.java
new file mode 100644
index 0000000..293e390
--- /dev/null
+++ b/annotation/annotation-experimental/src/main/java/androidx/annotation/UseExperimental.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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 androidx.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Allows use of an experimental API denoted by the given markers in the annotated file,
+ * declaration, or expression. If a declaration is annotated with {@link UseExperimental}, its
+ * usages are <strong>not</strong> required to opt-in to that experimental API.
+ */
+@Retention(CLASS)
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE})
+public @interface UseExperimental {
+    /**
+     * Defines the experimental API whose usage this annotation allows.
+     */
+    Class<?> markerClass();
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index adc1007..99f9209 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -29,7 +29,7 @@
  */
 val RELEASE_RULE = docsRules("public", false) {
     prebuilts(LibraryGroups.ACTIVITY, "1.0.0-beta01")
-    prebuilts(LibraryGroups.ANNOTATION, "1.1.0")
+    prebuilts(LibraryGroups.ANNOTATION, "annotation", "1.1.0")
     prebuilts(LibraryGroups.APPCOMPAT, "1.1.0-beta01")
     prebuilts(LibraryGroups.ARCH_CORE, "2.1.0-rc01")
     prebuilts(LibraryGroups.ASYNCLAYOUTINFLATER, "1.0.0")
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
index bba3289..82c658e 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
@@ -42,7 +42,8 @@
             "androidx.car_car-moderator.version",
             "androidx.activity_activity-ktx.version",
             "androidx.lifecycle_lifecycle-runtime-ktx.version",
-            "androidx.dynamicanimation_dynamicanimation-ktx.version"
+            "androidx.dynamicanimation_dynamicanimation-ktx.version",
+            "androidx.annotation_annotation-experimental.version"
         )
     }
 
@@ -83,4 +84,4 @@
         val newPath = Paths.get(dirPath, newFileName)
         file.updateRelativePath(newPath)
     }
-}
\ No newline at end of file
+}
diff --git a/settings.gradle b/settings.gradle
index 0dbef5c..67c8116 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -41,6 +41,9 @@
 includeProject(":ads-identifier", "ads/ads-identifier")
 includeProject(":annotation", "annotations")
 includeProject(":annotation:annotation-sampled", "annotation/annotation-sampled")
+includeProject(":annotation:annotation-experimental", "annotation/annotation-experimental")
+includeProject(":annotation:annotation-experimental-lint", "annotation/annotation-experimental-lint")
+includeProject(":annotation:annotation-experimental-lint-integration-tests", "annotation/annotation-experimental-lint/integration-tests")
 includeProject(":animation", "animation")
 includeProject(":animation:testing", "animation/testing")
 includeProject(":animation:integration-tests:testapp", "animation/integration-tests/testapp")