Merge "Revert "Fix suppressed lint checks for "ClassVerificationFailure"."" into androidx-main
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index 2bd8dca..b081506a 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -557,27 +557,6 @@
           wrapper-directory: ${{ env.project-root }}/gradle/wrapper
           wrapper-cache-enabled: true
 
-      - name: Set up Cloud SDK
-        uses: google-github-actions/setup-gcloud@master
-        if: ${{ github.repository == 'androidX/androidx' }}
-        with:
-          project_id: ${{ secrets.GCP_PROJECT_ID }}
-          service_account_key: ${{ secrets.GCP_SA_KEY }}
-          export_default_credentials: true
-      - name: "Run application tests on Firebase Test Lab"
-        uses: eskatos/gradle-command-action@v1
-        if: ${{ github.repository == 'androidX/androidx' }}
-        env:
-          JAVA_HOME: ${{ steps.setup-java.outputs.path }}
-        with:
-          arguments: firebaseTestLabTests ${{ needs.setup.outputs.gradlew_flags }}
-          build-root-directory: ${{ env.project-root }}
-          configuration-cache-enabled: true
-          dependencies-cache-enabled: true
-          gradle-executable: ${{ env.project-root }}/gradlew
-          wrapper-directory: ${{ env.project-root }}/gradle/wrapper
-          wrapper-cache-enabled: true
-
       - name: "upload build artifacts"
         continue-on-error: true
         if: always()
diff --git a/activity/activity-compose/build.gradle b/activity/activity-compose/build.gradle
index 20451ba..5b3d1b8 100644
--- a/activity/activity-compose/build.gradle
+++ b/activity/activity-compose/build.gradle
@@ -15,12 +15,8 @@
  */
 
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 import androidx.build.RunApiTasks
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("AndroidXPlugin")
@@ -32,7 +28,7 @@
 dependencies {
     kotlinPlugin projectOrArtifact(":compose:compiler:compiler")
 
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     api projectOrArtifact(":compose:runtime:runtime")
     api projectOrArtifact(":compose:runtime:runtime-saveable")
     api(projectOrArtifact(":activity:activity-ktx"))
@@ -42,10 +38,10 @@
     androidTestImplementation projectOrArtifact(":compose:material:material")
     androidTestImplementation projectOrArtifact(":compose:test-utils")
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_EXT_KTX)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testExtJunitKtx)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
 }
 
 androidx {
diff --git a/activity/activity-compose/integration-tests/activity-demos/build.gradle b/activity/activity-compose/integration-tests/activity-demos/build.gradle
index 82d727e..2928e80 100644
--- a/activity/activity-compose/integration-tests/activity-demos/build.gradle
+++ b/activity/activity-compose/integration-tests/activity-demos/build.gradle
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-
 import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.getKOTLIN_STDLIB
 
 plugins {
     id("AndroidXPlugin")
@@ -29,7 +25,7 @@
 
 dependencies {
     kotlinPlugin projectOrArtifact(":compose:compiler:compiler")
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation projectOrArtifact(":activity:activity-compose")
     implementation projectOrArtifact(":activity:activity-compose:activity-compose-samples")
 }
diff --git a/activity/activity-compose/samples/build.gradle b/activity/activity-compose/samples/build.gradle
index 9c9f8ee..a02eac6 100644
--- a/activity/activity-compose/samples/build.gradle
+++ b/activity/activity-compose/samples/build.gradle
@@ -14,13 +14,8 @@
  * limitations under the License.
  */
 
-
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("AndroidXPlugin")
@@ -31,7 +26,7 @@
 
 dependencies {
     kotlinPlugin projectOrArtifact(":compose:compiler:compiler")
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
 
     compileOnly projectOrArtifact(":annotation:annotation-sampled")
     implementation projectOrArtifact(":compose:foundation:foundation")
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index 33394d1..a422909 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -37,15 +35,15 @@
     api("androidx.savedstate:savedstate-ktx:1.1.0") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
-    api(KOTLIN_STDLIB)
+    api(libs.kotlinStdlib)
 
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.activity", module: "activity"
     })
diff --git a/activity/activity-lint/build.gradle b/activity/activity-lint/build.gradle
index 7e37c76..5e00727 100644
--- a/activity/activity-lint/build.gradle
+++ b/activity/activity-lint/build.gradle
@@ -17,22 +17,20 @@
 import androidx.build.LibraryGroups
 import androidx.build.LibraryType
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("kotlin")
 }
 
 dependencies {
-    compileOnly(LINT_API_MIN)
-    compileOnly(KOTLIN_STDLIB)
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.kotlinStdlib)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(LINT_CORE)
-    testImplementation(LINT_TESTS)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
 }
 
 androidx {
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index fd9d259..a9d5187 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -1,8 +1,6 @@
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -26,14 +24,14 @@
     implementation("androidx.tracing:tracing:1.0.0")
 
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(LEAKCANARY)
-    androidTestImplementation(LEAKCANARY_INSTRUMENTATION)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.leakcanary)
+    androidTestImplementation(libs.leakcanaryInstrumentation)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.activity", module: "activity"
     })
diff --git a/activity/integration-tests/testapp/build.gradle b/activity/integration-tests/testapp/build.gradle
index 11e5101..780dd7f 100644
--- a/activity/integration-tests/testapp/build.gradle
+++ b/activity/integration-tests/testapp/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -33,12 +31,12 @@
 dependencies {
     implementation(project(":activity:activity-ktx"))
     implementation(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx"))
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
 }
diff --git a/ads/ads-identifier-benchmark/build.gradle b/ads/ads-identifier-benchmark/build.gradle
index 95e55d2..57fdb60a 100644
--- a/ads/ads-identifier-benchmark/build.gradle
+++ b/ads/ads-identifier-benchmark/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -29,11 +27,11 @@
     androidTestImplementation(project(":ads-identifier-provider"))
     androidTestImplementation(project(":ads-identifier-testing"))
     androidTestImplementation("androidx.work:work-runtime:2.2.0")
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
 }
diff --git a/ads/ads-identifier-common/build.gradle b/ads/ads-identifier-common/build.gradle
index 264dff2..e238beb 100644
--- a/ads/ads-identifier-common/build.gradle
+++ b/ads/ads-identifier-common/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -28,11 +27,11 @@
     api("androidx.annotation:annotation:1.1.0")
 
     testImplementation(project(":ads-identifier-testing"))
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(MOCKITO_CORE)
-    testImplementation(ROBOLECTRIC)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.mockitoCore)
+    testImplementation(libs.robolectric)
 }
 
 android {
diff --git a/ads/ads-identifier-provider/build.gradle b/ads/ads-identifier-provider/build.gradle
index f1039b5..bcba6a9 100644
--- a/ads/ads-identifier-provider/build.gradle
+++ b/ads/ads-identifier-provider/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -27,19 +26,19 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.core:core:1.1.0")
-    implementation(AUTO_VALUE_ANNOTATIONS)
-    annotationProcessor(AUTO_VALUE)
+    implementation(libs.autoValueAnnotations)
+    annotationProcessor(libs.autoValue)
 
     implementation(project(":ads-identifier-common"))
 
     androidTestImplementation(project(":ads-identifier-testing"))
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
 }
 
 androidx {
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/build.gradle b/ads/ads-identifier-provider/integration-tests/testapp/build.gradle
index fa4e4a6..c5bd590ca 100644
--- a/ads/ads-identifier-provider/integration-tests/testapp/build.gradle
+++ b/ads/ads-identifier-provider/integration-tests/testapp/build.gradle
@@ -13,9 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
diff --git a/ads/ads-identifier-testing/build.gradle b/ads/ads-identifier-testing/build.gradle
index c54af2d..21e10e8 100644
--- a/ads/ads-identifier-testing/build.gradle
+++ b/ads/ads-identifier-testing/build.gradle
@@ -13,9 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -24,7 +21,7 @@
 dependencies {
     implementation(project(":ads-identifier-common"))
     api("androidx.annotation:annotation:1.1.0")
-    api(MOCKITO_CORE, excludes.bytebuddy)
+    api(libs.mockitoCore, excludes.bytebuddy)
 }
 
 android {
diff --git a/ads/ads-identifier/build.gradle b/ads/ads-identifier/build.gradle
index c844d83..b89c7f9 100644
--- a/ads/ads-identifier/build.gradle
+++ b/ads/ads-identifier/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -27,21 +26,21 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.core:core:1.1.0")
-    implementation(AUTO_VALUE_ANNOTATIONS)
-    annotationProcessor(AUTO_VALUE)
-    api(GUAVA_LISTENABLE_FUTURE)
+    implementation(libs.autoValueAnnotations)
+    annotationProcessor(libs.autoValue)
+    api(libs.guavaListenableFuture)
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
 
     implementation(project(":ads-identifier-common"))
 
     androidTestImplementation(project(":ads-identifier-testing"))
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
 }
 
 androidx {
diff --git a/ads/ads-identifier/integration-tests/testapp/build.gradle b/ads/ads-identifier/integration-tests/testapp/build.gradle
index c0fd225..13723ea 100644
--- a/ads/ads-identifier/integration-tests/testapp/build.gradle
+++ b/ads/ads-identifier/integration-tests/testapp/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -31,5 +29,5 @@
 dependencies {
     implementation(project(":ads-identifier"))
     implementation(project(":ads-identifier-common"))
-    implementation(GUAVA_ANDROID)
+    implementation(libs.guavaAndroid)
 }
diff --git a/androidx-plugin/build.gradle b/androidx-plugin/build.gradle
index 8dafbe6..3b6650b 100644
--- a/androidx-plugin/build.gradle
+++ b/androidx-plugin/build.gradle
@@ -16,7 +16,6 @@
     dependencies {
         classpath(libs.androidGradlePlugin)
         classpath(libs.kotlinGradlePlugin)
-        classpath(libs.kgpLeakPatcher)
         classpath(libs.dokkaGradlePlugin)
     }
 }
diff --git a/androidx-plugin/gradle-plugin/build.gradle b/androidx-plugin/gradle-plugin/build.gradle
index b98e295..bdc1739 100644
--- a/androidx-plugin/gradle-plugin/build.gradle
+++ b/androidx-plugin/gradle-plugin/build.gradle
@@ -20,7 +20,6 @@
     implementation(libs.androidGradlePlugin)
     implementation(libs.dexMemberList)
     implementation(libs.kotlinGradlePlugin)
-    implementation(libs.kgpLeakPatcher)
     implementation(libs.kotlinPoet)
 
     implementation(libs.dokkaGradlePlugin)
diff --git a/appcompat/appcompat-benchmark/build.gradle b/appcompat/appcompat-benchmark/build.gradle
index 1153708..341c82a 100644
--- a/appcompat/appcompat-benchmark/build.gradle
+++ b/appcompat/appcompat-benchmark/build.gradle
@@ -13,9 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 
 plugins {
     id("AndroidXPlugin")
@@ -27,9 +24,9 @@
 dependencies {
     androidTestImplementation(project(":appcompat:appcompat"))
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.kotlinStdlib)
 }
diff --git a/appcompat/appcompat-lint/build.gradle b/appcompat/appcompat-lint/build.gradle
index ca3e41d4..0a2edc8 100644
--- a/appcompat/appcompat-lint/build.gradle
+++ b/appcompat/appcompat-lint/build.gradle
@@ -18,8 +18,6 @@
 import androidx.build.LibraryType
 import androidx.build.LibraryVersions
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("kotlin")
@@ -27,13 +25,13 @@
 
 dependencies {
     // These checks are only compatible with the latest lint.
-    compileOnly(LINT_API_LATEST)
-    compileOnly(KOTLIN_STDLIB)
+    compileOnly(libs.androidLintApi)
+    compileOnly(libs.kotlinStdlib)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(LINT_CORE)
-    testImplementation(LINT_TESTS)
-    testImplementation(JUNIT)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
 }
 
 androidx {
diff --git a/appcompat/appcompat-resources/build.gradle b/appcompat/appcompat-resources/build.gradle
index 7cc3642..01ba76b 100644
--- a/appcompat/appcompat-resources/build.gradle
+++ b/appcompat/appcompat-resources/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -31,14 +30,14 @@
     api("androidx.vectordrawable:vectordrawable:1.1.0")
     api("androidx.vectordrawable:vectordrawable-animated:1.1.0")
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.espressoCore, excludes.espresso)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.appcompat", module: "appcompat-resources"
     })
diff --git a/appcompat/appcompat-resources/lint-baseline.xml b/appcompat/appcompat-resources/lint-baseline.xml
index 4982402..4826d47 100644
--- a/appcompat/appcompat-resources/lint-baseline.xml
+++ b/appcompat/appcompat-resources/lint-baseline.xml
@@ -112,94 +112,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 18; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat.AnimationDrawableTransition is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                anim.setAutoCancel(true);"
-        errorLine2="                     ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
-            line="401"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            state.mChangingConfigurations |= a.getChangingConfigurations();"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
-            line="452"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
-            line="541"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
-            line="591"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.appcompat.widget.ResourceManagerInternal.DrawableDelegate is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        drawable.inflate(context.getResources(), parser, attrs, theme);"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java"
-            line="565"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 15; however, the containing class androidx.appcompat.widget.ResourcesWrapper is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mResources.getValueForDensity(id, density, outValue, resolveRefs);"
-        errorLine2="                   ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ResourcesWrapper.java"
-            line="241"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.StateListDrawable is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            state.mChangingConfigurations |= a.getChangingConfigurations();"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java"
-            line="159"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.StateListDrawable is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    dr = Drawable.createFromXmlInner(r, parser, attrs, theme);"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java"
-            line="218"
-            column="35"/>
-    </issue>
-
-    <issue
         id="KotlinPropertyAccess"
         message="The getter return type (`Theme`) and setter parameter type (`int`) getter and setter methods for property `theme` should have exactly the same type to allow be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
         errorLine1="    public Resources.Theme getTheme() {"
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java
index f57aace..67c4235 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java
@@ -39,6 +39,7 @@
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.appcompat.resources.Compatibility;
 import androidx.appcompat.resources.R;
 import androidx.appcompat.widget.ResourceManagerInternal;
 import androidx.collection.LongSparseArray;
@@ -398,7 +399,7 @@
             final ObjectAnimator anim =
                     ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame);
             if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-                anim.setAutoCancel(true);
+                Compatibility.Api18Impl.setAutoCancel(anim, true);
             }
             anim.setDuration(interp.getTotalDuration());
             anim.setInterpolator(interp);
@@ -449,7 +450,7 @@
         final AnimatedStateListState state = mState;
         // Account for any configuration changes.
         if (SDK_INT >= LOLLIPOP) {
-            state.mChangingConfigurations |= a.getChangingConfigurations();
+            state.mChangingConfigurations |= Compatibility.Api21Impl.getChangingConfigurations(a);
         }
         // Extract the theme attributes, if any.
         state.setVariablePadding(
@@ -538,7 +539,7 @@
                 dr = AnimatedVectorDrawableCompat.createFromXmlInner(context, resources, parser,
                         attrs, theme);
             } else if (SDK_INT >= LOLLIPOP) {
-                dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);
+                dr = Compatibility.Api21Impl.createFromXmlInner(resources, parser, attrs, theme);
             } else {
                 dr = Drawable.createFromXmlInner(resources, parser, attrs);
             }
@@ -588,7 +589,7 @@
             if (parser.getName().equals("vector")) {
                 dr = VectorDrawableCompat.createFromXmlInner(resources, parser, attrs, theme);
             } else if (SDK_INT >= LOLLIPOP) {
-                dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);
+                dr = Compatibility.Api21Impl.createFromXmlInner(resources, parser, attrs, theme);
             } else {
                 dr = Drawable.createFromXmlInner(resources, parser, attrs);
             }
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java
index e501460..ceabac7 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java
@@ -35,6 +35,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.appcompat.resources.Compatibility;
 import androidx.appcompat.resources.R;
 import androidx.appcompat.widget.ResourceManagerInternal;
 
@@ -156,7 +157,7 @@
         final StateListState state = mStateListState;
         // Account for any configuration changes.
         if (SDK_INT >= LOLLIPOP) {
-            state.mChangingConfigurations |= a.getChangingConfigurations();
+            state.mChangingConfigurations |= Compatibility.Api21Impl.getChangingConfigurations(a);
         }
         state.mVariablePadding = a.getBoolean(
                 R.styleable.StateListDrawable_android_variablePadding, state.mVariablePadding);
@@ -215,7 +216,7 @@
                                     + "child tag defining a drawable");
                 }
                 if (SDK_INT >= LOLLIPOP) {
-                    dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+                    dr = Compatibility.Api21Impl.createFromXmlInner(r, parser, attrs, theme);
                 } else {
                     dr = Drawable.createFromXmlInner(r, parser, attrs);
                 }
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java
new file mode 100644
index 0000000..189b793
--- /dev/null
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 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.appcompat.resources;
+
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Unified ApiXXImpls for appcompat-resources.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public final class Compatibility {
+    private Compatibility() {
+        // This class is not instantiable.
+    }
+
+    @RequiresApi(21)
+    public static class Api21Impl {
+        private Api21Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        public static void inflate(@NonNull Drawable drawable, @NonNull Resources r,
+                @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
+                @Nullable Resources.Theme theme) throws IOException, XmlPullParserException {
+            drawable.inflate(r, parser, attrs, theme);
+        }
+
+        @DoNotInline
+        public static int getChangingConfigurations(@NonNull TypedArray typedArray) {
+            return typedArray.getChangingConfigurations();
+        }
+
+        @NonNull
+        @DoNotInline
+        public static Drawable createFromXmlInner(@NonNull Resources r,
+                @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
+                @Nullable Resources.Theme theme) throws IOException, XmlPullParserException {
+            return Drawable.createFromXmlInner(r, parser, attrs, theme);
+        }
+    }
+
+    @RequiresApi(18)
+    public static class Api18Impl {
+        private Api18Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        public static void setAutoCancel(@NonNull ObjectAnimator objectAnimator, boolean cancel) {
+            objectAnimator.setAutoCancel(cancel);
+        }
+    }
+
+    @RequiresApi(15)
+    public static class Api15Impl {
+        private Api15Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        public static void getValueForDensity(@NonNull Resources resources, int id, int density,
+                @NonNull TypedValue outValue, boolean resolveRefs) {
+            resources.getValueForDensity(id, density, outValue, resolveRefs);
+        }
+    }
+}
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java
index dfb0754..a269912 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java
@@ -38,6 +38,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat;
+import androidx.appcompat.resources.Compatibility;
 import androidx.appcompat.resources.R;
 import androidx.collection.LongSparseArray;
 import androidx.collection.LruCache;
@@ -562,7 +563,8 @@
                                     .asSubclass(Drawable.class);
                     Drawable drawable = drawableClass.getDeclaredConstructor().newInstance();
                     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-                        drawable.inflate(context.getResources(), parser, attrs, theme);
+                        Compatibility.Api21Impl.inflate(drawable, context.getResources(), parser,
+                                attrs, theme);
                     } else {
                         drawable.inflate(context.getResources(), parser, attrs);
                     }
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java
index d81cfe7..b854a9e 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java
@@ -29,6 +29,7 @@
 import android.util.TypedValue;
 
 import androidx.annotation.RequiresApi;
+import androidx.appcompat.resources.Compatibility;
 import androidx.core.content.res.ResourcesCompat;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -238,7 +239,7 @@
     @Override
     public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
-        mResources.getValueForDensity(id, density, outValue, resolveRefs);
+        Compatibility.Api15Impl.getValueForDensity(mResources, id, density, outValue, resolveRefs);
     }
 
     @Override
diff --git a/appcompat/appcompat/api/current.txt b/appcompat/appcompat/api/current.txt
index e1847bb..d206436 100644
--- a/appcompat/appcompat/api/current.txt
+++ b/appcompat/appcompat/api/current.txt
@@ -317,6 +317,7 @@
 
   public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
     ctor public AppCompatDialogFragment();
+    ctor public AppCompatDialogFragment(@LayoutRes int);
   }
 
   public class AppCompatViewInflater {
diff --git a/appcompat/appcompat/api/public_plus_experimental_current.txt b/appcompat/appcompat/api/public_plus_experimental_current.txt
index cc92740..6cb8e4a 100644
--- a/appcompat/appcompat/api/public_plus_experimental_current.txt
+++ b/appcompat/appcompat/api/public_plus_experimental_current.txt
@@ -317,6 +317,7 @@
 
   public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
     ctor public AppCompatDialogFragment();
+    ctor public AppCompatDialogFragment(@LayoutRes int);
   }
 
   public class AppCompatViewInflater {
diff --git a/appcompat/appcompat/api/restricted_current.txt b/appcompat/appcompat/api/restricted_current.txt
index 7fa4fdb..fdc45fa 100644
--- a/appcompat/appcompat/api/restricted_current.txt
+++ b/appcompat/appcompat/api/restricted_current.txt
@@ -341,6 +341,7 @@
 
   public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
     ctor public AppCompatDialogFragment();
+    ctor public AppCompatDialogFragment(@LayoutRes int);
   }
 
   public class AppCompatViewInflater {
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 7a35e42..cc71ddc 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -1,4 +1,3 @@
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -29,28 +28,27 @@
 
     kapt(project(":resourceinspection:resourceinspection-processor"))
 
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.espressoCore, excludes.espresso)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(project(":internal-testutils-appcompat"), {
         exclude group: "androidx.appcompat", module: "appcompat"
         exclude group: "androidx.core", module: "core"
     })
-    androidTestImplementation(MULTIDEX)
+    androidTestImplementation(libs.multidex)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(JUNIT)
-    testImplementation(ROBOLECTRIC)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.testCore)
+    testImplementation(libs.junit)
+    testImplementation(libs.robolectric)
 
-    // Uncomment once we want to start publishing the lint rules.
     lintPublish project(":appcompat:appcompat-lint")
 }
 
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index 7a8a2fb..590def4 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -239,6 +239,16 @@
             android:configChanges="uiMode"/>
 
         <activity
+            android:name="androidx.appcompat.app.NightModeUiModeConfigChangesActivityB"
+            android:theme="@style/Theme.AppCompat.DayNight"
+            android:configChanges="uiMode"/>
+
+        <activity
+            android:name="androidx.appcompat.app.NightModeUiModeConfigChangesActivityC"
+            android:theme="@style/Theme.AppCompat.DayNight"
+            android:configChanges="uiMode"/>
+
+        <activity
             android:name="androidx.appcompat.app.NightModeRotateDoesNotRecreateActivity"
             android:theme="@style/Theme.AppCompat.DayNight"
             android:configChanges="orientation|screenSize"/>
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogFragmentTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogFragmentTest.java
new file mode 100644
index 0000000..cf7049e
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogFragmentTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 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.appcompat.app;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Dialog;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.test.R;
+import androidx.fragment.app.DialogFragment;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatDialogFragmentTest {
+    @SuppressWarnings("deprecation")
+    @Rule
+    public final androidx.test.rule.ActivityTestRule<WindowDecorAppCompatActivity> mTestRule =
+            new androidx.test.rule.ActivityTestRule<>(WindowDecorAppCompatActivity.class);
+
+    private DialogFragment mFragment;
+
+    @Test
+    public void testDialogFragmentShows() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> mFragment = new TestDialogFragment()
+        );
+        mFragment.show(mTestRule.getActivity().getSupportFragmentManager(), null);
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        assertNotNull("Dialog was null", mFragment.getDialog());
+        assertTrue("Dialog was not being shown", mFragment.getDialog().isShowing());
+
+        // And make sure we dismiss the dialog
+        mFragment.dismissAllowingStateLoss();
+    }
+
+    @Test
+    public void testDialogFragmentWithLayout() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> mFragment = new AppCompatDialogFragment(R.layout.dialog_layout)
+        );
+        mFragment.show(mTestRule.getActivity().getSupportFragmentManager(), null);
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        assertNotNull("Dialog is not null", mFragment.getDialog());
+        assertTrue("Dialog is showing", mFragment.getDialog().isShowing());
+        assertNotNull("Dialog is using custom layout",
+                mFragment.getDialog().findViewById(R.id.dialog_content));
+
+        // And make sure we dismiss the dialog
+        mFragment.dismissAllowingStateLoss();
+    }
+
+    public static class TestDialogFragment extends AppCompatDialogFragment {
+        @NonNull
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return new AlertDialog.Builder(requireContext())
+                    .setTitle("Test")
+                    .setMessage("Message")
+                    .setPositiveButton("Button", null)
+                    .create();
+        }
+    }
+}
+
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/DialogTestCase.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/DialogTestCase.java
deleted file mode 100644
index 378cdf5..0000000
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/DialogTestCase.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2015 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.appcompat.app;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Dialog;
-import android.os.Bundle;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class DialogTestCase {
-    @Rule
-    public final ActivityTestRule<WindowDecorAppCompatActivity> mActivityTestRule =
-            new ActivityTestRule<>(WindowDecorAppCompatActivity.class);
-
-    private TestDialogFragment mFragment;
-
-    @Test
-    public void testDialogFragmentShows() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        mFragment = new TestDialogFragment();
-                    }
-                }
-        );
-        mFragment.show(mActivityTestRule.getActivity().getSupportFragmentManager(), null);
-
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
-        assertNotNull("Dialog was null", mFragment.getDialog());
-        assertTrue("Dialog was not being shown", mFragment.getDialog().isShowing());
-
-        // And make sure we dismiss the dialog
-        mFragment.dismissAllowingStateLoss();
-    }
-
-    public static class TestDialogFragment extends AppCompatDialogFragment {
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            return new AlertDialog.Builder(getContext())
-                    .setTitle("Test")
-                    .setMessage("Message")
-                    .setPositiveButton("Button", null)
-                    .create();
-        }
-    }
-}
-
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LayoutInflaterFactoryTestCase.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LayoutInflaterFactoryTestCase.java
index 15d4194..8b17ada 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LayoutInflaterFactoryTestCase.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LayoutInflaterFactoryTestCase.java
@@ -43,7 +43,6 @@
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -52,9 +51,11 @@
 
 @RunWith(AndroidJUnit4.class)
 public class LayoutInflaterFactoryTestCase {
+
+    @SuppressWarnings("deprecation")
     @Rule
-    public final ActivityTestRule<LayoutInflaterFactoryTestActivity> mActivityTestRule =
-            new ActivityTestRule<>(LayoutInflaterFactoryTestActivity.class);
+    public final androidx.test.rule.ActivityTestRule<LayoutInflaterFactoryTestActivity> mTestRule =
+            new androidx.test.rule.ActivityTestRule<>(LayoutInflaterFactoryTestActivity.class);
 
     @Before
     public void setup() {
@@ -66,7 +67,7 @@
     @Test
     @SmallTest
     public void testAndroidThemeInflation() {
-        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+        final LayoutInflater inflater = LayoutInflater.from(mTestRule.getActivity());
         assertThemedContext(inflater.inflate(R.layout.layout_android_theme, null));
     }
 
@@ -74,7 +75,7 @@
     @Test
     @SmallTest
     public void testAppThemeInflation() {
-        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+        final LayoutInflater inflater = LayoutInflater.from(mTestRule.getActivity());
         assertThemedContext(inflater.inflate(R.layout.layout_app_theme, null));
     }
 
@@ -83,7 +84,7 @@
     @Test
     @SmallTest
     public void testAndroidThemeWithChildrenInflation() {
-        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mTestRule.getActivity());
         final ViewGroup root = (ViewGroup) inflater.inflate(
                 R.layout.layout_android_theme_children, null);
         assertThemedContext(root);
@@ -93,7 +94,7 @@
     @Test
     @SmallTest
     public void testAndroidThemeWithIncludeInflation() {
-        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mTestRule.getActivity());
         final ViewGroup root = (ViewGroup) inflater.inflate(
                 R.layout.layout_android_theme_with_include, null);
         assertThemedContext(root.findViewById(R.id.included_view));
@@ -102,8 +103,19 @@
     @UiThreadTest
     @Test
     @SmallTest
+    public void testAndroidThemeWithMergeInflation() {
+        LayoutInflater inflater = LayoutInflater.from(mTestRule.getActivity());
+        final ViewGroup root = (ViewGroup) inflater.inflate(
+                R.layout.layout_android_theme_with_merge, null);
+        assertThemedContext(root.findViewById(R.id.merged_view));
+        assertThemedContext(root.findViewById(R.id.merged_view_2));
+    }
+
+    @UiThreadTest
+    @Test
+    @SmallTest
     public void testThemedInflationWithUnattachedParent() {
-        final Context activity = mActivityTestRule.getActivity();
+        final Context activity = mTestRule.getActivity();
 
         // Create a parent but not attached
         final LinearLayout parent = new LinearLayout(activity);
@@ -205,15 +217,15 @@
     @Test
     @SmallTest
     public void testDeclarativeOnClickWithContextWrapper() {
-        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mTestRule.getActivity());
         View view = inflater.inflate(R.layout.layout_button_themed_onclick, null);
 
         assertTrue(view.performClick());
-        assertTrue(mActivityTestRule.getActivity().wasDeclarativeOnClickCalled());
+        assertTrue(mTestRule.getActivity().wasDeclarativeOnClickCalled());
     }
 
     private void verifyAppCompatWidgetInflation(final int layout, final Class<?> expectedClass) {
-        LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+        LayoutInflater inflater = LayoutInflater.from(mTestRule.getActivity());
         View view = inflater.inflate(layout, null);
         assertSame("View is " + expectedClass.getSimpleName(), expectedClass,
                 view.getClass());
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt
index 62f9285..d078551 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-@file:Suppress("DEPRECATION")
+@file:Suppress("deprecation")
 
 package androidx.appcompat.app
 
@@ -23,14 +23,19 @@
 import android.app.Instrumentation.ActivityMonitor
 import android.content.Intent
 import android.content.res.Configuration
+import android.os.Handler
+import android.os.Looper
 import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
 import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
 import androidx.appcompat.testutils.NightModeUtils
+import androidx.lifecycle.Lifecycle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
+import androidx.testutils.LifecycleOwnerUtils.waitUntilState
 import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNotSame
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -84,7 +89,7 @@
         )
 
         // From activity A, start activity B.
-        var activityA = monitorA.waitForActivityWithTimeout(3000) as NightModeActivity
+        val activityA = monitorA.waitForActivityWithTimeout(3000) as NightModeActivity
         assertNotNull(activityA)
         activityA.startActivity(
             Intent(instr.context, NightModeActivityB::class.java).apply {
@@ -92,6 +97,9 @@
             }
         )
 
+        // Activity A is hidden, wait for it to stop.
+        waitUntilState(activityA, Lifecycle.State.CREATED)
+
         // From activity B, start activity C.
         val activityB = monitorB.waitForActivityWithTimeout(3000) as NightModeActivity
         assertNotNull(activityB)
@@ -101,6 +109,9 @@
             }
         )
 
+        // Activity B is hidden, wait for it to stop.
+        waitUntilState(activityB, Lifecycle.State.CREATED)
+
         // Toggle default night mode.
         val activityC = monitorC.waitForActivityWithTimeout(3000) as NightModeActivity
         assertNotNull(activityC)
@@ -111,39 +122,196 @@
         // Activity C should receive a configuration change.
         activityC.expectOnConfigurationChange(3000)
 
-        // Activities A and B should recreate().
+        // Activities A and B should recreate() in the background.
         val activityA2 = expectRecreate(monitorA, activityA) as NightModeActivity
         val activityB2 = expectRecreate(monitorB, activityB) as NightModeActivity
 
         // Activity C should have received a night mode configuration change.
-        activityC.runOnUiThread {
-            NightModeUtils.assertConfigurationNightModeEquals(
-                "Activity A's effective configuration has night mode set",
-                Configuration.UI_MODE_NIGHT_YES,
-                activityC.effectiveConfiguration!!
-            )
-        }
-
-        // Activity A should have been recreated in night mode.
-        activityA2.runOnUiThread {
-            NightModeUtils.assertConfigurationNightModeEquals(
-                "Activity A's effective configuration has night mode set",
-                Configuration.UI_MODE_NIGHT_YES,
-                activityA2.effectiveConfiguration!!
-            )
-        }
-
-        // Activity B should have been recreated in night mode.
-        activityB2.runOnUiThread {
-            NightModeUtils.assertConfigurationNightModeEquals(
-                "Activity B's effective configuration has night mode set",
-                Configuration.UI_MODE_NIGHT_YES,
-                activityB2.effectiveConfiguration!!
-            )
+        listOf(activityC, activityA2, activityB2).forEach { activity ->
+            activityC.runOnUiThread {
+                NightModeUtils.assertConfigurationNightModeEquals(
+                    "Activity ${activity.title}'s effective configuration has night mode set",
+                    Configuration.UI_MODE_NIGHT_YES,
+                    activityC.effectiveConfiguration!!
+                )
+            }
         }
     }
 
-    fun expectRecreate(monitor: ActivityMonitor, activity: Activity): Activity {
+    /**
+     * Regression test for the following scenario:
+     *
+     * If you have a stack of activities where every activity has `android:configChanges="uiMode"`
+     * and you call [AppCompatDelegate.setDefaultNightMode] from thread other than the top
+     * activity, then it can cause the bottom activity to not receive `onConfigurationChanged`.
+     *
+     * Eg:
+     * - Activity A DOES intercept uiMode config changes in manifest
+     * - Activity B DOES as well
+     * - Activity C DOES as well
+     *
+     * Here is your stack : A > B > C (C on top)
+     *
+     * Call [AppCompatDelegate.setDefaultNightMode] with a new mode on activity C (but not directly
+     * from this activity, ex with RX AndroidSchedulers.mainThread or an handler). Activity C
+     * receives both `onConfigurationChanged` and `onNightModeChanged`, but activities A and B
+     * may not receive either callback or change their configurations.
+     *
+     * Process:
+     * 1. A > B > C > setDefaultNightMode YES
+     * 2. Go back to A (B & C destroyed) > B > C > setDefaultNightMode NO (wrong config for A)
+     * 3. repeat (YES/NO/YES/NO...)
+     */
+    @Test
+    @SdkSuppress(minSdkVersion = 17)
+    public fun testDefaultNightModeWithStackedActivitiesAndNavigation() {
+        val instr = InstrumentationRegistry.getInstrumentation()
+        val result = Instrumentation.ActivityResult(0, Intent())
+        val monitorA = ActivityMonitor(
+            NightModeUiModeConfigChangesActivity::class.java.name,
+            result, false
+        )
+        val monitorB = ActivityMonitor(
+            NightModeUiModeConfigChangesActivityB::class.java.name,
+            result, false
+        )
+        val monitorC = ActivityMonitor(
+            NightModeUiModeConfigChangesActivityC::class.java.name,
+            result, false
+        )
+        instr.addMonitor(monitorA)
+        instr.addMonitor(monitorB)
+        instr.addMonitor(monitorC)
+
+        instr.runOnMainSync {
+            AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
+        }
+
+        // Start activity A.
+        instr.startActivitySync(
+            Intent(instr.context, NightModeUiModeConfigChangesActivity::class.java).apply {
+                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                putExtra(NightModeActivity.KEY_TITLE, "A")
+            }
+        )
+
+        // From activity A, start activity B.
+        val activityA = monitorA.waitForActivityWithTimeout(3000) as NightModeActivity
+        assertNotNull("Activity A started within 3000ms", activityA)
+        activityA.startActivity(
+            Intent(instr.context, NightModeUiModeConfigChangesActivityB::class.java).apply {
+                putExtra(NightModeActivity.KEY_TITLE, "B")
+            }
+        )
+
+        // Activity A is hidden, wait for it to stop.
+        waitUntilState(activityA, Lifecycle.State.CREATED)
+
+        // From activity B, start activity C.
+        val activityB = monitorB.waitForActivityWithTimeout(3000) as NightModeActivity
+        assertNotNull("Activity B started within 3000ms", activityB)
+        activityB.startActivity(
+            Intent(instr.context, NightModeUiModeConfigChangesActivityC::class.java).apply {
+                putExtra(NightModeActivity.KEY_TITLE, "C")
+            }
+        )
+
+        // Activity B is hidden, wait for it to stop.
+        waitUntilState(activityB, Lifecycle.State.CREATED)
+
+        // Wait for activity C to start.
+        val activityC = monitorC.waitForActivityWithTimeout(3000) as NightModeActivity
+        assertNotNull("Activity C started within 3000ms", activityC)
+
+        // Toggle default night mode from a non-UI thread.
+        Handler(Looper.getMainLooper()).post {
+            AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES)
+        }
+
+        // Activities A, B, and C should all receive configuration changes.
+        listOf(activityA, activityB, activityC).forEach { activity ->
+            activity.expectOnConfigurationChange(3000)
+        }
+
+        // Activities A, B, and C should have all received the new configuration.
+        listOf(activityA, activityB, activityC).forEach { activity ->
+            activity.runOnUiThread {
+                NightModeUtils.assertConfigurationNightModeEquals(
+                    "Activity ${activity.title}'s effective configuration has night mode set",
+                    Configuration.UI_MODE_NIGHT_YES,
+                    activity.effectiveConfiguration!!
+                )
+            }
+        }
+
+        // Tear down activities C and B, in that order.
+        listOf(activityC, activityB).forEach { activity ->
+            activity.runOnUiThread {
+                activity.finish()
+            }
+            waitUntilState(activity, Lifecycle.State.DESTROYED)
+        }
+
+        // Activity A is in the foreground, wait for it to resume.
+        waitUntilState(activityA, Lifecycle.State.RESUMED)
+
+        // From activity A, start activity B again.
+        activityA.startActivity(
+            Intent(instr.context, NightModeUiModeConfigChangesActivityB::class.java).apply {
+                putExtra(NightModeActivity.KEY_TITLE, "B2")
+            }
+        )
+
+        // Activity A is hidden, wait for it to stop.
+        waitUntilState(activityA, Lifecycle.State.CREATED)
+
+        // From activity B, start activity C. Double-check the return, since the monitor could
+        // trigger on Activity B's lifecycle if the platform does something unexpected.
+        val activityB2 = monitorB.waitForActivityWithTimeout(3000) as NightModeActivity
+        assertNotSame("Monitor responded to activity B2 lifecycle", activityB, activityB2)
+        assertNotNull("Activity B2 started within 3000ms", activityB2)
+        activityB2.startActivity(
+            Intent(instr.context, NightModeUiModeConfigChangesActivityC::class.java).apply {
+                putExtra(NightModeActivity.KEY_TITLE, "C2")
+            }
+        )
+
+        // Activity B is hidden, wait for it to stop.
+        waitUntilState(activityB2, Lifecycle.State.CREATED)
+
+        // Wait for activity C to start. Double-check the return.
+        val activityC2 = monitorC.waitForActivityWithTimeout(3000) as NightModeActivity
+        assertNotSame("Monitor responded to Activity C2 lifecycle", activityC, activityC2)
+        assertNotNull("Activity C2 started within 3000ms", activityC2)
+
+        // Prepare activities A, B, and C to track configuration changes.
+        listOf(activityA, activityB2, activityC2).forEach { activity ->
+            activity.resetOnConfigurationChange()
+        }
+
+        // Toggle default night mode again from a non-UI thread.
+        Handler(Looper.getMainLooper()).post {
+            AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
+        }
+
+        // Activities A, B, and C should all receive configuration changes.
+        listOf(activityA, activityB2, activityC2).forEach { activity ->
+            activity.expectOnConfigurationChange(3000)
+        }
+
+        // Activities A, B, and C should have all received the new configuration.
+        listOf(activityA, activityB2, activityC2).forEach { activity ->
+            activity.runOnUiThread {
+                NightModeUtils.assertConfigurationNightModeEquals(
+                    "Activity ${activity.title}'s effective configuration has night mode set",
+                    Configuration.UI_MODE_NIGHT_NO,
+                    activity.effectiveConfiguration!!
+                )
+            }
+        }
+    }
+
+    private fun expectRecreate(monitor: ActivityMonitor, activity: Activity): Activity {
         // The documentation says "Block until an Activity is created that matches this monitor."
         // This statement is true, but there are some other true statements like: "Block until an
         // Activity is destroyed" or "Block until an Activity is resumed"...
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityB.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityB.java
new file mode 100644
index 0000000..a5d27ef
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityB.java
@@ -0,0 +1,22 @@
+/*
+ * 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.appcompat.app;
+
+/**
+ * An activity with DayNight theme that handles uiMode configuration changes.
+ */
+public class NightModeUiModeConfigChangesActivityB extends NightModeActivity {}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityC.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityC.java
new file mode 100644
index 0000000..83b473c
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityC.java
@@ -0,0 +1,22 @@
+/*
+ * 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.appcompat.app;
+
+/**
+ * An activity with DayNight theme that handles uiMode configuration changes.
+ */
+public class NightModeUiModeConfigChangesActivityC extends NightModeActivity {}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt
index 0cec4f9..fe35b47 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt
@@ -71,27 +71,51 @@
     }
 
     @Test
-    fun testOnConfigurationChangeNotCalledWhenNotStarted() {
+    fun testOnConfigurationChangeCalledWhileStopped() {
+        scenario.moveToState(Lifecycle.State.RESUMED)
         scenario.moveToState(Lifecycle.State.CREATED)
-        // And clear any previous config changes
-        scenario.onActivity { it.lastConfigurationChangeAndClear }
 
         // Set local night mode to YES
         scenario.onActivity { setNightMode(MODE_NIGHT_YES, it, setMode) }
-        // Assert that the onConfigurationChange was not called with a new correct config
+        // Assert that the onConfigurationChange was called with a new correct config
         scenario.onActivity {
-            assertNull(it.lastConfigurationChangeAndClear)
+            val lastConfig = it.lastConfigurationChangeAndClear
+            assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES, lastConfig!!)
         }
 
         // Set local night mode back to NO
         scenario.onActivity { setNightMode(MODE_NIGHT_NO, it, setMode) }
-        // Assert that the onConfigurationChange was not called with a new correct config
+        // Assert that the onConfigurationChange was called with a new correct config
         scenario.onActivity {
-            assertNull(it.lastConfigurationChangeAndClear)
+            val lastConfig = it.lastConfigurationChangeAndClear
+            assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_NO, lastConfig!!)
         }
     }
 
     @Test
+    fun testOnConfigurationChangeNotCalledWhileDestroyed() {
+        scenario.moveToState(Lifecycle.State.RESUMED)
+
+        lateinit var activity: NightModeUiModeConfigChangesActivity
+        scenario.onActivity { activity = it }
+
+        scenario.moveToState(Lifecycle.State.DESTROYED)
+
+        // And clear any previous config changes
+        activity.lastConfigurationChangeAndClear
+
+        // Set local night mode to YES
+        setNightMode(MODE_NIGHT_YES, activity, setMode)
+        // Assert that the onConfigurationChange was not called with a new correct config
+        assertNull(activity.lastConfigurationChangeAndClear)
+
+        // Set local night mode back to NO
+        setNightMode(MODE_NIGHT_NO, activity, setMode)
+        // Assert that the onConfigurationChange was not called with a new correct config
+        assertNull(activity.lastConfigurationChangeAndClear)
+    }
+
+    @Test
     fun testResourcesUpdated() {
         // Set local night mode to YES
         scenario.onActivity { setNightMode(MODE_NIGHT_YES, it, setMode) }
@@ -128,7 +152,9 @@
     @After
     fun cleanup() {
         // Reset the default night mode
-        scenario.onActivity { setNightMode(MODE_NIGHT_NO, it, NightSetMode.DEFAULT) }
+        if (scenario.state != Lifecycle.State.DESTROYED) {
+            scenario.onActivity { setNightMode(MODE_NIGHT_NO, it, NightSetMode.DEFAULT) }
+        }
         scenario.close()
     }
 
diff --git a/appcompat/appcompat/src/androidTest/res/layout/dialog_layout.xml b/appcompat/appcompat/src/androidTest/res/layout/dialog_layout.xml
new file mode 100644
index 0000000..b97647f
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/res/layout/dialog_layout.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/dialog_content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+    <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="24sp"
+            android:textColor="@color/test_green"
+            android:text="@string/alert_dialog_custom_text1"
+            android:singleLine="false" />
+    <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="20sp"
+            android:textColor="@color/test_blue"
+            android:text="@string/alert_dialog_custom_text2"
+            android:singleLine="false" />
+</LinearLayout>
diff --git a/appcompat/appcompat/src/androidTest/res/layout/layout_android_theme_merged_views.xml b/appcompat/appcompat/src/androidTest/res/layout/layout_android_theme_merged_views.xml
new file mode 100644
index 0000000..37c5c1e
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/res/layout/layout_android_theme_merged_views.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <TextView
+        android:id="@+id/merged_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Test" />
+
+    <TextView
+        android:id="@+id/merged_view_2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Test 2" />
+
+</merge>
\ No newline at end of file
diff --git a/appcompat/appcompat/src/androidTest/res/layout/layout_android_theme_with_merge.xml b/appcompat/appcompat/src/androidTest/res/layout/layout_android_theme_with_merge.xml
new file mode 100644
index 0000000..c9d3511
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/res/layout/layout_android_theme_with_merge.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:theme="@style/MagentaThemeOverlay">
+
+    <include layout="@layout/layout_android_theme_merged_views"
+        android:id="@+id/included_view" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index 08f06a8..7ca7f37 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -244,10 +244,10 @@
     private boolean mLongPressBackDown;
 
     private boolean mBaseContextAttached;
+    // true after the first call to onCreated.
     private boolean mCreated;
-    private boolean mStarted;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    boolean mIsDestroyed;
+    // true after the first (and only) call to onDestroyed.
+    boolean mDestroyed;
 
     /**
      * The configuration from the most recent call to either onConfigurationChanged or onCreate.
@@ -669,8 +669,6 @@
 
     @Override
     public void onStart() {
-        mStarted = true;
-
         // This will apply day/night if the time has changed, it will also call through to
         // setupAutoNightModeIfNeeded()
         applyDayNight();
@@ -678,8 +676,6 @@
 
     @Override
     public void onStop() {
-        mStarted = false;
-
         ActionBar ab = getSupportActionBar();
         if (ab != null) {
             ab.setShowHideAnimationEnabled(false);
@@ -743,8 +739,7 @@
             mWindow.getDecorView().removeCallbacks(mInvalidatePanelMenuRunnable);
         }
 
-        mStarted = false;
-        mIsDestroyed = true;
+        mDestroyed = true;
 
         if (mLocalNightMode != MODE_NIGHT_UNSPECIFIED
                 && mHost instanceof Activity
@@ -844,7 +839,7 @@
             // A pending invalidation will typically be resolved before the posted message
             // would run normally in order to satisfy instance state restoration.
             PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
-            if (!mIsDestroyed && (st == null || st.menu == null)) {
+            if (!mDestroyed && (st == null || st.menu == null)) {
                 invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
             }
         }
@@ -1185,7 +1180,7 @@
     @Override
     public boolean onMenuItemSelected(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
         final Window.Callback cb = getWindowCallback();
-        if (cb != null && !mIsDestroyed) {
+        if (cb != null && !mDestroyed) {
             final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
             if (panel != null) {
                 return cb.onMenuItemSelected(panel.featureId, item);
@@ -1247,7 +1242,7 @@
         }
 
         ActionMode mode = null;
-        if (mAppCompatCallback != null && !mIsDestroyed) {
+        if (mAppCompatCallback != null && !mDestroyed) {
             try {
                 mode = mAppCompatCallback.onWindowStartingSupportActionMode(callback);
             } catch (AbstractMethodError ame) {
@@ -1653,7 +1648,7 @@
 
     private void openPanel(final PanelFeatureState st, KeyEvent event) {
         // Already open, return
-        if (st.isOpen || mIsDestroyed) {
+        if (st.isOpen || mDestroyed) {
             return;
         }
 
@@ -1765,7 +1760,7 @@
             final Window.Callback cb = getWindowCallback();
 
             if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
-                if (cb != null && !mIsDestroyed) {
+                if (cb != null && !mDestroyed) {
                     // If we have a menu invalidation pending, do it now.
                     if (mInvalidatePanelMenuPosted &&
                             (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
@@ -1785,7 +1780,7 @@
                 }
             } else {
                 mDecorContentParent.hideOverflowMenu();
-                if (!mIsDestroyed) {
+                if (!mDestroyed) {
                     final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
                     cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, st.menu);
                 }
@@ -1866,7 +1861,7 @@
     }
 
     private boolean preparePanel(PanelFeatureState st, KeyEvent event) {
-        if (mIsDestroyed) {
+        if (mDestroyed) {
             return false;
         }
 
@@ -1977,7 +1972,7 @@
         mClosingActionMenu = true;
         mDecorContentParent.dismissPopups();
         Window.Callback cb = getWindowCallback();
-        if (cb != null && !mIsDestroyed) {
+        if (cb != null && !mDestroyed) {
             cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, menu);
         }
         mClosingActionMenu = false;
@@ -2041,7 +2036,7 @@
                 mDecorContentParent.canShowOverflowMenu() &&
                 !ViewConfiguration.get(mContext).hasPermanentMenuKey()) {
             if (!mDecorContentParent.isOverflowMenuShowing()) {
-                if (!mIsDestroyed && preparePanel(st, event)) {
+                if (!mDestroyed && preparePanel(st, event)) {
                     handled = mDecorContentParent.showOverflowMenu();
                 }
             } else {
@@ -2104,7 +2099,7 @@
             return;
         }
 
-        if (!mIsDestroyed) {
+        if (!mDestroyed) {
             // We need to be careful which callback we dispatch the call to. We can not dispatch
             // this to the Window's callback since that will call back into this method and cause a
             // crash. Instead we need to dispatch down to the original Activity/Dialog/etc.
@@ -2385,7 +2380,7 @@
 
     @SuppressWarnings("deprecation")
     private boolean applyDayNight(final boolean allowRecreation) {
-        if (mIsDestroyed) {
+        if (mDestroyed) {
             if (DEBUG) {
                 Log.d(TAG, "applyDayNight. Skipping because host is destroyed");
             }
@@ -2614,14 +2609,15 @@
         if (callOnConfigChange && mHost instanceof Activity) {
             final Activity activity = (Activity) mHost;
             if (activity instanceof LifecycleOwner) {
-                // If the Activity is a LifecyleOwner, check that it is at least started
+                // If the Activity is a LifecyleOwner, check that it is after onCreate() and
+                // before onDestroy(), which includes STOPPED.
                 Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
-                if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
+                if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.CREATED)) {
                     activity.onConfigurationChanged(conf);
                 }
             } else {
-                // Otherwise we'll fallback to our internal started flag.
-                if (mStarted) {
+                // Otherwise, we'll fallback to our internal created and destroyed flags.
+                if (mCreated && !mDestroyed) {
                     activity.onConfigurationChanged(conf);
                 }
             }
@@ -2777,7 +2773,7 @@
             // Only dispatch for the root menu
             if (subMenu == subMenu.getRootMenu() && mHasActionBar) {
                 Window.Callback cb = getWindowCallback();
-                if (cb != null && !mIsDestroyed) {
+                if (cb != null && !mDestroyed) {
                     cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
                 }
             }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java
index feb05f0..48c6835 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java
@@ -24,6 +24,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
@@ -37,6 +38,16 @@
  */
 public class AppCompatDialogFragment extends DialogFragment {
 
+    /** {@inheritDoc} **/
+    public AppCompatDialogFragment() {
+        super();
+    }
+
+    /** {@inheritDoc} **/
+    public AppCompatDialogFragment(@LayoutRes int contentLayoutId) {
+        super(contentLayoutId);
+    }
+
     @NonNull
     @Override
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
diff --git a/appcompat/integration-tests/receive-content-testapp/build.gradle b/appcompat/integration-tests/receive-content-testapp/build.gradle
index 5b887ff..6e9dbe7 100644
--- a/appcompat/integration-tests/receive-content-testapp/build.gradle
+++ b/appcompat/integration-tests/receive-content-testapp/build.gradle
@@ -31,15 +31,15 @@
     api("androidx.annotation:annotation:1.1.0")
     implementation(project(":appcompat:appcompat"))
     implementation(CONSTRAINT_LAYOUT, { transitive = true })
-    implementation(GUAVA_ANDROID)
+    implementation(libs.guavaAndroid)
     implementation(project(":recyclerview:recyclerview"))
-    implementation(MATERIAL)
+    implementation(libs.material)
 
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE)
-    implementation(ESPRESSO_IDLING_RESOURCE)
-    implementation(TRUTH)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.espressoCore)
+    implementation(libs.espressoIdlingResource)
+    implementation(libs.truth)
 }
diff --git a/benchmark/benchmark/build.gradle b/benchmark/benchmark/build.gradle
index 5af72d9..09799ac 100644
--- a/benchmark/benchmark/build.gradle
+++ b/benchmark/benchmark/build.gradle
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("AndroidXPlugin")
@@ -33,9 +32,9 @@
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
     androidTestImplementation(project(":tracing:tracing-ktx"))
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.kotlinStdlib)
 }
diff --git a/benchmark/common/build.gradle b/benchmark/common/build.gradle
index ebeeee7..6d47166 100644
--- a/benchmark/common/build.gradle
+++ b/benchmark/common/build.gradle
@@ -15,12 +15,9 @@
  */
 
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -28,15 +25,15 @@
 }
 
 dependencies {
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     api("androidx.annotation:annotation:1.1.0")
     api("androidx.annotation:annotation-experimental:1.0.0")
     implementation("androidx.tracing:tracing-ktx:1.0.0")
-    implementation(ANDROIDX_TEST_MONITOR)
+    implementation(libs.testMonitor)
 
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(KOTLIN_TEST)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.kotlinTest)
 }
 
 androidx {
diff --git a/benchmark/gradle-plugin/build.gradle b/benchmark/gradle-plugin/build.gradle
index 49db110..55ac348 100644
--- a/benchmark/gradle-plugin/build.gradle
+++ b/benchmark/gradle-plugin/build.gradle
@@ -14,13 +14,7 @@
  * limitations under the License.
  */
 
-import androidx.build.BuildOnServerKt
-import androidx.build.BuildServerConfigurationKt
-import androidx.build.LibraryGroups
-import androidx.build.LibraryType
-import androidx.build.SdkResourceGenerator
-
-import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.*
 
 plugins {
     id("AndroidXPlugin")
@@ -34,13 +28,13 @@
     implementation(findGradleKotlinDsl())
     implementation(gradleApi())
     implementation("com.android.tools.build:gradle:4.0.0-beta04")
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
 
     testImplementation(gradleTestKit())
     testImplementation(project(":internal-testutils-gradle-plugin"))
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(KOTLIN_TEST)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.kotlinTest)
 }
 
 SdkResourceGenerator.generateForHostTest(project)
diff --git a/benchmark/integration-tests/crystalball-experiment/build.gradle b/benchmark/integration-tests/crystalball-experiment/build.gradle
index ed90325..a721908 100644
--- a/benchmark/integration-tests/crystalball-experiment/build.gradle
+++ b/benchmark/integration-tests/crystalball-experiment/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
@@ -63,11 +62,11 @@
 }
 
 dependencies {
-    api(JUNIT)
-    api(KOTLIN_STDLIB)
+    api(libs.junit)
+    api(libs.kotlinStdlib)
     api("androidx.annotation:annotation:1.1.0")
     // TODO: remove, once we remove the minor usages in CrystalBall
-    implementation(GUAVA_ANDROID)
+    implementation(libs.guavaAndroid)
     androidTestImplementation("com.android:collector-device-lib:0.1.0")
     androidTestImplementation("com.android:collector-device-lib-platform:0.1.0")
     androidTestImplementation("com.android:collector-helper-utilities:0.1.0")
@@ -84,8 +83,8 @@
     androidTestImplementation("com.android:microbenchmark-device-lib:0.1.0")
     androidTestImplementation("androidx.test:rules:1.3.0")
     androidTestImplementation("androidx.test:runner:1.3.0")
-    implementation(ANDROIDX_TEST_EXT_JUNIT)
-    implementation(ANDROIDX_TEST_UIAUTOMATOR)
+    implementation(libs.testExtJunit)
+    implementation(libs.testUiautomator)
 }
 
 androidx {
diff --git a/benchmark/integration-tests/dry-run-benchmark/build.gradle b/benchmark/integration-tests/dry-run-benchmark/build.gradle
index 8a4a70c..37da74aa 100644
--- a/benchmark/integration-tests/dry-run-benchmark/build.gradle
+++ b/benchmark/integration-tests/dry-run-benchmark/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -25,8 +23,8 @@
 
 dependencies {
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.kotlinStdlib)
 }
diff --git a/benchmark/integration-tests/macrobenchmark-target/build.gradle b/benchmark/integration-tests/macrobenchmark-target/build.gradle
index 13f9ec5..a9204d8 100644
--- a/benchmark/integration-tests/macrobenchmark-target/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark-target/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.CONSTRAINT_LAYOUT
 
 plugins {
     id("AndroidXPlugin")
@@ -33,10 +33,10 @@
 }
 
 dependencies {
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation(CONSTRAINT_LAYOUT, { transitive = true })
     implementation("androidx.arch.core:core-runtime:2.1.0")
     implementation("androidx.appcompat:appcompat:1.2.0")
     implementation("androidx.recyclerview:recyclerview:1.1.0")
-    implementation(MATERIAL)
+    implementation(libs.material)
 }
diff --git a/benchmark/integration-tests/macrobenchmark/build.gradle b/benchmark/integration-tests/macrobenchmark/build.gradle
index 3067b8a..3627a44 100644
--- a/benchmark/integration-tests/macrobenchmark/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark/build.gradle
@@ -14,13 +14,6 @@
  * limitations under the License.
  */
 
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryGroups
-import androidx.build.Publish
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -38,11 +31,11 @@
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
     androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testUiautomator)
 }
 
 // Define a task dependency so the app is installed before we run macro benchmarks.
diff --git a/benchmark/integration-tests/startup-benchmark/build.gradle b/benchmark/integration-tests/startup-benchmark/build.gradle
index 8a4a70c..37da74aa 100644
--- a/benchmark/integration-tests/startup-benchmark/build.gradle
+++ b/benchmark/integration-tests/startup-benchmark/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -25,8 +23,8 @@
 
 dependencies {
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.kotlinStdlib)
 }
diff --git a/benchmark/junit4/build.gradle b/benchmark/junit4/build.gradle
index a3f0f10..425cc63 100644
--- a/benchmark/junit4/build.gradle
+++ b/benchmark/junit4/build.gradle
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -34,15 +32,15 @@
 dependencies {
     api(project(":benchmark:benchmark-common"))
 
-    api(JUNIT)
-    api(KOTLIN_STDLIB)
+    api(libs.junit)
+    api(libs.kotlinStdlib)
 
     implementation("androidx.test:rules:1.3.0")
     implementation("androidx.test:runner:1.3.0")
     implementation("androidx.tracing:tracing-ktx:1.0.0")
     api("androidx.annotation:annotation:1.1.0")
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(libs.testExtJunit)
 }
 
 androidx {
diff --git a/benchmark/macro-junit4/build.gradle b/benchmark/macro-junit4/build.gradle
index d159e9f..8f3927e 100644
--- a/benchmark/macro-junit4/build.gradle
+++ b/benchmark/macro-junit4/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
@@ -34,8 +33,8 @@
 }
 
 dependencies {
-    api(JUNIT)
-    api(KOTLIN_STDLIB)
+    api(libs.junit)
+    api(libs.kotlinStdlib)
     api("androidx.annotation:annotation:1.1.0")
     api(project(":benchmark:benchmark-macro"))
     implementation(project(":benchmark:benchmark-common"))
@@ -46,13 +45,13 @@
     androidTestImplementation(project(":internal-testutils-ktx"))
     androidTestImplementation(project(":tracing:tracing-ktx"))
     androidTestImplementation("androidx.test:rules:1.3.0")
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
     // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
     // DexMaker has it"s own MockMaker
 }
 
diff --git a/benchmark/macro/build.gradle b/benchmark/macro/build.gradle
index 947bbd9..c5998dd 100644
--- a/benchmark/macro/build.gradle
+++ b/benchmark/macro/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 import androidx.build.SupportConfigKt
@@ -64,21 +63,21 @@
 }
 
 dependencies {
-    api(JUNIT)
-    api(KOTLIN_STDLIB)
+    api(libs.junit)
+    api(libs.kotlinStdlib)
     api("androidx.annotation:annotation:1.1.0")
 
     implementation(project(":benchmark:benchmark-common"))
-    implementation(ANDROIDX_TEST_CORE)
-    implementation(ANDROIDX_TEST_UIAUTOMATOR)
+    implementation(libs.testCore)
+    implementation(libs.testUiautomator)
     implementation(libs.wireRuntime)
 
     androidTestImplementation(project(":internal-testutils-ktx"))
     androidTestImplementation(project(":tracing:tracing-ktx"))
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(KOTLIN_TEST)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.kotlinTest)
 }
 
 androidx {
diff --git a/browser/browser/build.gradle b/browser/browser/build.gradle
index c98ffa8..c900186 100644
--- a/browser/browser/build.gradle
+++ b/browser/browser/build.gradle
@@ -1,6 +1,4 @@
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -21,28 +19,28 @@
 dependencies {
     api("androidx.core:core:1.1.0")
     api("androidx.annotation:annotation:1.1.0")
-    api(GUAVA_LISTENABLE_FUTURE)
+    api(libs.guavaListenableFuture)
 
     implementation("androidx.collection:collection:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.interpolator:interpolator:1.0.0")
 
-    annotationProcessor(NULLAWAY)
+    annotationProcessor(libs.nullaway)
 
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.mockitoCore)
 
     androidTestImplementation("androidx.appcompat:appcompat:1.0.0")
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.espressoCore, excludes.espresso)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(project(":internal-testutils-runtime"))
 }
 
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index c460134..71fdf91 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -10,7 +10,6 @@
 
     dependencies {
         classpath(libs.kotlinGradlePlugin)
-        classpath(libs.kgpLeakPatcher) // KT-46368
     }
 
     configurations.classpath.resolutionStrategy {
@@ -79,14 +78,12 @@
     cacheableRuntimeOnly(libs.hiltAndroidGradlePlugin)
     // room kotlintestapp uses the ksp plugin but it does not publish a plugin marker yet
     cacheableApi(libs.kspGradlePlugin)
-    cacheableApi(libs.kgpLeakPatcher)
     // dependencies whose resolutions we don't need to cache
     compileOnly(findGradleKotlinDsl()) // Only one file in this configuration, no need to cache it
     implementation(project("jetpad-integration")) // Doesn't have a .pom, so not slow to load
 }
 
 apply plugin: "java-gradle-plugin"
-apply plugin: "dev.zacsweers.kgp-150-leak-patcher"
 
 sourceSets {
     main.java.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/kotlin"
diff --git a/buildSrc/dependencies.gradle b/buildSrc/dependencies.gradle
index 23c0358..5a1aaae 100644
--- a/buildSrc/dependencies.gradle
+++ b/buildSrc/dependencies.gradle
@@ -31,4 +31,10 @@
     exclude group: "androidx.core"
 }
 
+excludes.mlkit = {
+    exclude group: "androidx.fragment"
+    exclude group: "androidx.core"
+    exclude group: "androidx.exifinterface"
+}
+
 rootProject.ext["excludes"] = excludes
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt
index ea6c673..c17b7b7 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt
@@ -155,6 +155,8 @@
 
     var legacyDisableKotlinStrictApiMode = false
 
+    var benchmarkRunAlsoInterpreted = false
+
     fun shouldEnforceKotlinStrictApiMode(): Boolean {
         return !legacyDisableKotlinStrictApiMode &&
             shouldConfigureApiTasks()
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt
index e229ac9..b7b0d63 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt
@@ -17,7 +17,6 @@
 package androidx.build
 
 import androidx.build.AndroidXRootPlugin.Companion.PROJECT_OR_ARTIFACT_EXT_NAME
-import androidx.build.ftl.FirebaseTestLabHelper
 import androidx.build.gradle.getByType
 import androidx.build.gradle.isRoot
 import com.android.build.gradle.LibraryExtension
@@ -71,9 +70,8 @@
         config = PlaygroundProperties.load(rootProject)
         repos = PlaygroundRepositories(config)
         rootProject.repositories.addPlaygroundRepositories()
-        val ftlUtilities = FirebaseTestLabHelper(target)
         rootProject.subprojects {
-            configureSubProject(it, ftlUtilities)
+            configureSubProject(it)
         }
 
         // TODO(b/185539993): Re-enable InvalidFragmentVersionForActivityResult which was
@@ -99,10 +97,7 @@
         }
     }
 
-    private fun configureSubProject(
-        project: Project,
-        firebaseTestLabHelper: FirebaseTestLabHelper
-    ) {
+    private fun configureSubProject(project: Project) {
         project.repositories.addPlaygroundRepositories()
         project.extra.set(PROJECT_OR_ARTIFACT_EXT_NAME, projectOrArtifactClosure)
         project.configurations.all { configuration ->
@@ -110,7 +105,6 @@
                 substitution.replaceIfSnapshot()
             }
         }
-        firebaseTestLabHelper.setupFTL(project)
     }
 
     /**
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 2dc4895..eb13252 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -230,9 +230,6 @@
                 configureAndroidLibraryWithMultiplatformPluginOptions()
             }
         }
-
-        // https://youtrack.jetbrains.com/issue/KT-46368
-        project.apply(plugin = "dev.zacsweers.kgp-150-leak-patcher")
     }
 
     @Suppress("UnstableApiUsage") // AGP DSL APIs
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
index 0f973b4..94644fb5 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
@@ -38,8 +38,12 @@
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 import java.io.File
 import java.util.concurrent.ConcurrentHashMap
+import org.gradle.build.event.BuildEventsListenerRegistry
 
-class AndroidXRootPlugin : Plugin<Project> {
+abstract class AndroidXRootPlugin : Plugin<Project> {
+    @get:javax.inject.Inject
+    abstract val registry: BuildEventsListenerRegistry
+
     override fun apply(project: Project) {
         if (!project.isRoot) {
             throw Exception("This plugin should only be applied to root project")
@@ -219,7 +223,7 @@
 
         registerStudioTask()
 
-        TaskUpToDateValidator.setup(project)
+        TaskUpToDateValidator.setup(project, registry)
 
         project.tasks.register("listTaskOutputs", ListTaskOutputsTask::class.java) { task ->
             task.setOutput(File(project.getDistributionDirectory(), "task_outputs.txt"))
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
index a152e67..996b451b 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
@@ -128,9 +128,6 @@
                 apply(plugin = "org.jetbrains.kotlin.android")
             }
 
-            // https://youtrack.jetbrains.com/issue/KT-46368
-            apply(plugin = "dev.zacsweers.kgp-150-leak-patcher")
-
             configureManifests()
             if (isMultiplatformEnabled) {
                 configureForMultiplatform()
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 78bd208..a2a0b1a 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -48,7 +48,7 @@
     val CONTENTPAGER = Version("1.1.0-alpha01")
     val COMPOSE = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.0.0-beta08")
     val COORDINATORLAYOUT = Version("1.2.0-alpha01")
-    val CORE = Version("1.7.0-alpha01")
+    val CORE = Version("1.6.0-beta02")
     val CORE_ANIMATION = Version("1.0.0-alpha03")
     val CORE_ANIMATION_TESTING = Version("1.0.0-alpha03")
     val CORE_APPDIGEST = Version("1.0.0-alpha01")
@@ -105,6 +105,7 @@
     val SAVEDSTATE = Version("1.2.0-alpha01")
     val SECURITY = Version("1.1.0-alpha04")
     val SECURITY_APP_AUTHENTICATOR = Version("1.0.0-alpha02")
+    val SECURITY_APP_AUTHENTICATOR_TESTING = Version("1.0.0-alpha01")
     val SECURITY_BIOMETRIC = Version("1.0.0-alpha01")
     val SECURITY_IDENTITY_CREDENTIAL = Version("1.0.0-alpha02")
     val SHARETARGET = Version("1.2.0-alpha01")
@@ -113,7 +114,7 @@
     val SLICE_BUILDERS_KTX = Version("1.0.0-alpha08")
     val SLICE_REMOTECALLBACK = Version("1.0.0-alpha01")
     val SLIDINGPANELAYOUT = Version("1.2.0-alpha03")
-    val STARTUP = Version("1.1.0-beta02")
+    val STARTUP = Version("1.1.0-rc01")
     val SQLITE = Version("2.2.0-alpha01")
     val SQLITE_INSPECTOR = Version("2.1.0-alpha01")
     val SWIPEREFRESHLAYOUT = Version("1.2.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt b/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
index 3323df1..62196e2 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
@@ -22,7 +22,6 @@
 import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.SetProperty
 import org.gradle.api.tasks.Classpath
-import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.TaskAction
@@ -53,10 +52,6 @@
     @InputFiles
     lateinit var sourcesDir: File
 
-    // String representing names of .md files to be included in documentation
-    @Input
-    lateinit var includes: String
-
     // Directory containing the docs project and package-lists
     @InputFiles
     lateinit var docsProjectDir: File
@@ -84,7 +79,9 @@
             linksConfiguration +=
                 "${it.value}^${docsProjectDir.toPath()}/package-lists/${it.key}/package-list^^"
         }
-
+        val includes = sourcesDir.walkTopDown()
+            .filter { it.name.endsWith("documentation.md") }
+            .joinToString(";")
         val includesString = if (includes.isNotEmpty()) { "-includes $includes" } else { "" }
 
         return listOf(
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 6c676ff..fbb8c91 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -87,7 +87,7 @@
  */
 internal lateinit var kspVersion: String
 val KSP_VERSION get() = kspVersion
-const val KOTLIN_GRADLE_PLUGIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0"
+const val KOTLIN_GRADLE_PLUGIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
 
 const val KOTLIN_METADATA_JVM = "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.3.0"
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
index da31e71..5ce9779 100644
--- a/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
@@ -312,9 +312,6 @@
                 destinationDir = generatedDocsDir
                 samplesDir = unzippedSamplesSources
                 sourcesDir = unzippedDocsSources
-                includes = unzippedDocsSources.walkTopDown()
-                    .filter { it.name.endsWith("documentation.md") }
-                    .joinToString(";")
                 docsProjectDir = File(project.rootDir, "docs-public")
                 dependenciesClasspath = androidJarFile(project) + dependencyClasspath
             }
@@ -562,7 +559,7 @@
     }
 }
 
-private const val DACKKA_DEPENDENCY = "com.google.devsite:dackka:0.0.4"
+private const val DACKKA_DEPENDENCY = "com.google.devsite:dackka:0.0.5"
 private const val DOCLAVA_DEPENDENCY = "com.android:doclava:1.0.6"
 
 // Allowlist for directories that should be processed by Dackka
@@ -570,6 +567,7 @@
     "androidx/benchmark/**",
     "androidx/collection/**",
     "androidx/compose/**",
+    "androidx/datastore/**",
     "androidx/lifecycle/**",
     "androidx/navigation/**",
     "androidx/paging/**",
diff --git a/buildSrc/src/main/kotlin/androidx/build/ftl/FirebaseTestLabHelper.kt b/buildSrc/src/main/kotlin/androidx/build/ftl/FirebaseTestLabHelper.kt
deleted file mode 100644
index 04ab48b..0000000
--- a/buildSrc/src/main/kotlin/androidx/build/ftl/FirebaseTestLabHelper.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2021 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.build.ftl
-
-import androidx.build.gradle.isRoot
-import com.android.build.gradle.TestedExtension
-import org.gradle.api.Project
-
-/**
- * Helper class to setup Firebase Test Lab for instrumentation tests
- */
-internal class FirebaseTestLabHelper(
-    private val rootProject: Project
-) {
-    init {
-        check(rootProject.isRoot) {
-            "FTL Utilities can only be created for root projects"
-        }
-    }
-
-    private val anchorTask by lazy {
-        rootProject.tasks.register(ANCHOR_TASK_NAME) {
-            it.description = "Anchor task that depends on all firebase test lab tests"
-            it.group = "Verification"
-        }
-    }
-
-    fun setupFTL(project: Project) {
-        AGP_PLUGIN_IDS.forEach { agpPluginId ->
-            // using base plugin at this stage does not work as base plugin is applied before the
-            // Android Extension is created.
-            // see the comment on [AGP_PLUGIN_IDS] for details.
-            project.pluginManager.withPlugin(agpPluginId) {
-                project.extensions.findByType(TestedExtension::class.java)?.let {
-                    configure(project, it)
-                }
-            }
-        }
-    }
-
-    private fun configure(project: Project, testedExtension: TestedExtension) {
-        testedExtension.testVariants.all { testVariant ->
-            RunTestOnFTLTask.create(project, testVariant)?.let { ftlTask ->
-                anchorTask.configure { it.dependsOn(ftlTask) }
-            }
-        }
-    }
-
-    companion object {
-        const val ANCHOR_TASK_NAME = "firebaseTestLabTests"
-
-        /**
-         * AGP base plugin is applied before the extension is created so instead we use plugin
-         * ids here.
-         * see: https://github.com/google/ksp/issues/314
-         * see: https://github.com/google/ksp/pull/318
-         */
-        private val AGP_PLUGIN_IDS = listOf(
-            "com.android.application",
-            // TODO enable library and dynamic feature when we can synthesize
-            //  an APK for them
-            //  "com.android.library",
-            //  "com.android.dynamic-feature"
-        )
-    }
-}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/ftl/GCloudCLIWrapper.kt b/buildSrc/src/main/kotlin/androidx/build/ftl/GCloudCLIWrapper.kt
deleted file mode 100644
index 385e271..0000000
--- a/buildSrc/src/main/kotlin/androidx/build/ftl/GCloudCLIWrapper.kt
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright 2021 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.build.ftl
-
-import androidx.build.ftl.GCloudCLIWrapper.RunTestParameters.Companion.TEST_OUTPUT_FILE_NAME
-import com.google.gson.Gson
-import com.google.gson.annotations.SerializedName
-import com.google.gson.reflect.TypeToken
-import org.gradle.api.GradleException
-import org.gradle.process.ExecOperations
-import java.io.ByteArrayOutputStream
-import java.io.File
-import java.util.Locale
-import java.util.UUID
-
-/**
- * Wrapper around GCloud CLI.
- *
- * https://cloud.google.com/sdk/gcloud
- *
- * Note that this wrapper requires gcloud to be available on the host machine.
- *
- * documentation for FTL:
- * https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run
- */
-@Suppress("UnstableApiUsage") // ExecOperations
-internal class GCloudCLIWrapper(
-    private val execOperations: ExecOperations
-) {
-    private val gson = Gson()
-
-    /**
-     * Path to the gcloud executable, derived from `which gcloud` call.
-     */
-    private val gcloud: String by lazy {
-        findExecutable("gcloud")
-    }
-
-    /**
-     * Path to the gsutil executable, derived from `which gsutil` call.
-     */
-    private val gsutil: String by lazy {
-        findExecutable("gsutil")
-    }
-
-    private inline fun <reified T> executeGcloud(
-        vararg params: String
-    ): T {
-        val output = ByteArrayOutputStream()
-        val errorOutput = ByteArrayOutputStream()
-        val execResult = execOperations.exec {
-            it.executable = gcloud
-            it.args = params.toList() + "--format=json"
-            it.standardOutput = output
-            it.errorOutput = errorOutput
-            it.isIgnoreExitValue = true
-        }
-        if (execResult.exitValue != 0) {
-            System.err.println("GCloud command failed: ${errorOutput.toString(Charsets.UTF_8)}")
-        }
-        // still try to parse the because when it fails (e.g. test failure), it returns a non-0
-        // exit code but still prints the output. We are interested in the output.
-        val commandOutput = output.toString(Charsets.UTF_8)
-        return gson.parse(commandOutput)
-    }
-
-    private fun execGsUtil(
-        vararg params: String
-    ): String {
-        val output = ByteArrayOutputStream()
-        execOperations.exec {
-            it.executable = gsutil
-            it.args = params.toList()
-            it.standardOutput = output
-        }
-        return output.toString(Charsets.UTF_8)
-    }
-
-    /**
-     * https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run
-     */
-    fun runTest(
-        params: RunTestParameters
-    ): List<TestResult> {
-        val testResults = executeGcloud<List<TestResult>>(
-            "firebase", "test", "android", "run",
-            "--type", "instrumentation",
-            "--test", params.testApk.canonicalPath,
-            "--app", params.testedApk.canonicalPath,
-            "--num-flaky-test-attempts", "2",
-            "--results-bucket=${params.bucketName}",
-            "--results-dir=${params.resultsBucketDir}",
-            "--results-history-name=${params.projectPath}"
-        )
-        // copy the test results from the bucket to the build directory
-        execGsUtil(
-            "cp", "-r", params.cloudBucketPath() + "/*", params.resultsLocalDir.canonicalPath
-        )
-        // finally, write the command response into the directory as well
-        val testResultOutput = params.resultsLocalDir.resolve(TEST_OUTPUT_FILE_NAME)
-        testResultOutput.bufferedWriter(Charsets.UTF_8).use {
-            gson.toJson(
-                testResults,
-                it
-            )
-        }
-        return testResults
-    }
-
-    /**
-     * find the given executable's path in the PATH via `which` command.
-     */
-    private fun findExecutable(name: String): String {
-        val output = ByteArrayOutputStream()
-        val result = execOperations.exec {
-            it.commandLine("which", name)
-            it.standardOutput = output
-            it.isIgnoreExitValue = true
-        }
-        if (result.exitValue != 0) {
-            throw GradleException(
-                """
-                Unable to find $name CLI executable.
-                `which $name` returned exit code ${result.exitValue}.
-                Make sure gcloud CLI is installed, authenticated and is part of your PATH.
-                See https://cloud.google.com/sdk/gcloud for installation instructions.
-                """.trimIndent()
-            )
-        }
-        return output.toString(Charsets.UTF_8).trim()
-    }
-
-    /**
-     * Data structure format for gcloud FTL command
-     */
-    internal data class TestResult(
-        @SerializedName("axis_value")
-        val axisValue: String,
-        val outcome: String,
-        @SerializedName("test_details")
-        val testDetails: String
-    ) {
-        val passed
-            get() = outcome.toLowerCase(Locale.US) in SUCCESS_OUTCOMES
-
-        companion object {
-            private val SUCCESS_OUTCOMES = listOf("passed", "flaky")
-        }
-    }
-
-    /**
-     * Parameters for invoking a test on the Firebase Test Lab
-     */
-    internal data class RunTestParameters(
-        /**
-         * The project path for which we are executing the tests for.
-         */
-        val projectPath: String,
-        /**
-         * The tested APK file
-         */
-        val testedApk: File,
-        /**
-         * The test APK file which includes the instrumentation tests
-         */
-        val testApk: File,
-        /**
-         * The name of the GS bucket to save the results
-         */
-        val bucketName: String = DEFAULT_BUCKET_NAME,
-        /**
-         * The GS Bucket directory where the results will be saved
-         */
-        val resultsBucketDir: String = buildRelativeResultDirPath(projectPath),
-        /**
-         * The local directory where we will download the test results
-         */
-        val resultsLocalDir: File,
-    ) {
-
-        /**
-         * Returns the path to the Google Cloud bucket where the test run results are saved
-         */
-        fun cloudBucketPath(): String {
-            return "gs://$bucketName/$resultsBucketDir"
-        }
-
-        companion object {
-            const val DEFAULT_BUCKET_NAME = "androidx-ftl-test-results"
-
-            /**
-             * The file into which the result of the gcloud command will be written.
-             */
-            const val TEST_OUTPUT_FILE_NAME = "testResults.json"
-
-            /**
-             * Generates a relative path for test results.
-             *
-             * If run on Github Actions CI, this method will use the environment variables to
-             * create a unique path for the action.
-             * If run locally, this will create a random UUID for the directory.
-             */
-            private fun buildRelativeResultDirPath(
-                projectPath: String
-            ): String {
-                // github action env variables:
-                // https://docs.github.com/en/actions/reference/environment-variables
-                val inGithubActions = System.getenv().containsKey("GITHUB_ACTIONS")
-                val pathValues = if (inGithubActions) {
-                    val workflowName = requireEnvValue("GITHUB_WORKFLOW")
-                    val runNumber = requireEnvValue("GITHUB_RUN_NUMBER")
-                    val runId = requireEnvValue("GITHUB_RUN_ID")
-                    val ref = System.getenv("GITHUB_REF")
-                    listOfNotNull(
-                        "github",
-                        projectPath,
-                        ref,
-                        workflowName,
-                        runNumber,
-                        runId,
-                    )
-                } else {
-                    listOf(
-                        "local",
-                        projectPath,
-                        UUID.randomUUID().toString()
-                    )
-                }
-                return pathValues.joinToString("/")
-            }
-
-            private fun requireEnvValue(name: String): String {
-                return System.getenv(name) ?: throw GradleException(
-                    "Cannot find required environment variable: $name"
-                )
-            }
-        }
-    }
-}
-
-private inline fun <reified T> Gson.parse(
-    input: String
-): T {
-    val typeToken = object : TypeToken<T>() {}.type
-    return this.fromJson(input, typeToken)
-}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/ftl/RunTestOnFTLTask.kt b/buildSrc/src/main/kotlin/androidx/build/ftl/RunTestOnFTLTask.kt
deleted file mode 100644
index 8ec207d..0000000
--- a/buildSrc/src/main/kotlin/androidx/build/ftl/RunTestOnFTLTask.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright 2021 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.build.ftl
-
-import androidx.build.getDistributionDirectory
-import androidx.build.getSupportRootFolder
-import com.android.build.gradle.api.ApkVariant
-import com.android.build.gradle.api.ApkVariantOutput
-import com.android.build.gradle.api.TestVariant
-import org.gradle.api.DefaultTask
-import org.gradle.api.GradleException
-import org.gradle.api.Project
-import org.gradle.api.file.DirectoryProperty
-import org.gradle.api.file.RegularFileProperty
-import org.gradle.api.provider.Property
-import org.gradle.api.tasks.CacheableTask
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.PathSensitive
-import org.gradle.api.tasks.PathSensitivity
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.TaskProvider
-import org.gradle.process.ExecOperations
-import org.gradle.workers.WorkAction
-import org.gradle.workers.WorkParameters
-import org.gradle.workers.WorkerExecutor
-import javax.inject.Inject
-
-/**
- * Task to run instrumentation tests on FTL.
- *
- * This task is only enabled on playground projects and requires gcloud CLI to be available on
- * the device with the right permissions.
- *
- * Due to the limitations of FTL, this task only support application instrumentation tests for now.
- */
-@Suppress("UnstableApiUsage") // for gradle property APIs
-@CacheableTask
-abstract class RunTestOnFTLTask @Inject constructor(
-    private val workerExecutor: WorkerExecutor
-) : DefaultTask() {
-    /**
-     * The test APK for the instrumentation test.
-     */
-    @get:[InputFile PathSensitive(PathSensitivity.NONE)]
-    abstract val testApk: RegularFileProperty
-
-    /**
-     * The tested application APK.
-     */
-    @get:[InputFile PathSensitive(PathSensitivity.NONE)]
-    abstract val testedApk: RegularFileProperty
-
-    /**
-     * Output file to write the results
-     */
-    @get:OutputDirectory
-    abstract val testResults: DirectoryProperty
-
-    @TaskAction
-    fun executeTest() {
-        workerExecutor.noIsolation().submit(
-            RunFTLTestWorkAction::class.java
-        ) {
-            it.testApk.set(testApk)
-            it.testedApk.set(testedApk)
-            it.testResults.set(testResults)
-            it.projectPath.set(project.relativeResultPath())
-        }
-    }
-
-    interface RunFTLTestParams : WorkParameters {
-        val projectPath: Property<String>
-        val testApk: RegularFileProperty
-        val testedApk: RegularFileProperty
-        val testResults: DirectoryProperty
-    }
-
-    abstract class RunFTLTestWorkAction @Inject constructor(
-        private val execOperations: ExecOperations
-    ) : WorkAction<RunFTLTestParams> {
-        override fun execute() {
-            val localTestResultDir = parameters.testResults.asFile.get()
-            localTestResultDir.apply {
-                deleteRecursively()
-                mkdirs()
-            }
-            val testApk = parameters.testApk.asFile.get()
-            val testedApk = parameters.testedApk.asFile.get()
-            val gcloud = GCloudCLIWrapper(execOperations)
-            val params = GCloudCLIWrapper.RunTestParameters(
-                testedApk = testedApk,
-                testApk = testApk,
-                projectPath = parameters.projectPath.get(),
-                resultsLocalDir = localTestResultDir
-
-            )
-            val result = gcloud.runTest(params)
-            val failed = result.filterNot {
-                it.passed
-            }
-            if (failed.isNotEmpty()) {
-                throw GradleException("These tests failed: $failed")
-            }
-        }
-    }
-
-    companion object {
-        private const val TASK_SUFFIX = "OnFirebaseTestLab"
-
-        /**
-         * Creates an FTL test runner task and returns it.
-         * Note that only application tests are supported hence this will return `null` for
-         * library projects.
-         */
-        fun create(project: Project, testVariant: TestVariant): TaskProvider<RunTestOnFTLTask>? {
-            // TODO add support for library project, which might require synthesizing another
-            //  APK :facepalm:
-            // see: // https://stackoverflow.com/questions/59827750/execute-instrumented-test-for-an-android-library-with-firebase-test-lab
-            val testedVariant = testVariant.testedVariant as? ApkVariant
-                ?: return null
-            val taskName = testVariant.name + TASK_SUFFIX
-            val testResultDir = project.layout.buildDirectory.dir(
-                "ftl-results"
-            )
-            // create task to copy results into dist directory
-            val copyToDistTask = project.tasks.register(
-                "copyResultsOf${taskName}ToDist",
-                Copy::class.java
-            ) {
-                it.description = "Copy test results from $taskName into DIST folder"
-                it.group = "build"
-                it.from(testResultDir)
-                it.into(
-                    project.getDistributionDirectory()
-                        .resolve("ftl-results/${project.relativeResultPath()}/$taskName")
-                )
-            }
-            return project.tasks.register(taskName, RunTestOnFTLTask::class.java) { task ->
-                task.description = "Run ${testVariant.name} tests on Firebase Test Lab"
-                task.group = "Verification"
-                task.testResults.set(testResultDir)
-                task.dependsOn(testVariant.packageApplicationProvider)
-                task.dependsOn(testedVariant.packageApplicationProvider)
-
-                task.testApk.set(
-                    testVariant.outputs
-                        .withType(ApkVariantOutput::class.java)
-                        .firstOrNull()
-                        ?.outputFile
-                )
-                task.testedApk.set(
-                    testedVariant.outputs
-                        .withType(ApkVariantOutput::class.java)
-                        .firstOrNull()
-                        ?.outputFile
-                )
-                task.finalizedBy(copyToDistTask)
-            }
-        }
-    }
-}
-
-/**
- * Returns the relative path of the project wrt the support root. This path is used for both
- * local dist path and cloud bucket paths.
- */
-private fun Project.relativeResultPath() = projectDir.relativeTo(
-    project.getSupportRootFolder()
-).path
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
index 17c29c3..9cd747d 100644
--- a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -67,6 +67,10 @@
     abstract val hasBenchmarkPlugin: Property<Boolean>
 
     @get:Input
+    @get:Optional
+    abstract val benchmarkRunAlsoInterpreted: Property<Boolean>
+
+    @get:Input
     abstract val testRunner: Property<String>
 
     @get:Input
@@ -127,6 +131,9 @@
         if (hasBenchmarkPlugin.get()) {
             configBuilder.isBenchmark(true)
             if (configBuilder.isPostsubmit) {
+                if (benchmarkRunAlsoInterpreted.get()) {
+                    configBuilder.tag("microbenchmarks_interpreted")
+                }
                 configBuilder.tag("microbenchmarks")
             }
         } else if (testProjectPath.get().endsWith("macrobenchmark")) {
diff --git a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
index ff5b301..e2a730e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
@@ -18,6 +18,7 @@
 
 package androidx.build.testConfiguration
 
+import androidx.build.AndroidXExtension
 import androidx.build.AndroidXPlugin
 import androidx.build.AndroidXPlugin.Companion.ZIP_CONSTRAINED_TEST_CONFIGS_WITH_APKS_TASK
 import androidx.build.AndroidXPlugin.Companion.ZIP_TEST_CONFIGS_WITH_APKS_TASK
@@ -82,7 +83,13 @@
         } else {
             task.minSdk.set(minSdk)
         }
-        task.hasBenchmarkPlugin.set(this.hasBenchmarkPlugin())
+        val hasBenchmarkPlugin = this.hasBenchmarkPlugin()
+        task.hasBenchmarkPlugin.set(hasBenchmarkPlugin)
+        if (hasBenchmarkPlugin) {
+            task.benchmarkRunAlsoInterpreted.set(
+                extensions.getByType<AndroidXExtension>().benchmarkRunAlsoInterpreted
+            )
+        }
         task.testRunner.set(testRunner)
         task.testProjectPath.set(this.path)
         task.affectedModuleDetectorSubset.set(
diff --git a/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt b/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
index 75e35dde..1752636 100644
--- a/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
@@ -16,14 +16,15 @@
 
 package androidx.build.uptodatedness
 
-import androidx.build.VERIFY_UP_TO_DATE
 import org.gradle.api.GradleException
 import org.gradle.api.Project
 import org.gradle.api.Task
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.kotlin.dsl.extra
-import java.io.File
-import java.util.Date
+import org.gradle.api.services.BuildService
+import org.gradle.api.services.BuildServiceParameters
+import org.gradle.build.event.BuildEventsListenerRegistry
+import org.gradle.tooling.events.FinishEvent
+import org.gradle.tooling.events.OperationCompletionListener
+import org.gradle.tooling.events.task.TaskExecutionResult
 
 /**
  * Validates that all tasks (except a temporary exception list) are considered up-to-date.
@@ -34,7 +35,6 @@
  */
 
 const val DISALLOW_TASK_EXECUTION_FLAG_NAME = "disallowExecution"
-const val RECORD_FLAG_NAME = VERIFY_UP_TO_DATE
 
 // Temporary set of exempt tasks that are known to still be out-of-date after running once
 // Entries in this set may be task names (like assembleRelease) or task paths
@@ -144,7 +144,12 @@
     ":appsearch:appsearch-local-storage:buildCMakeDebug[icing]",
     ":appsearch:appsearch-local-storage:buildCMakeRelWithDebInfo[icing]",
     ":hilt:hilt-navigation-compose:kaptGenerateStubsDebugKotlin",
-    ":hilt:hilt-navigation-compose:kaptGenerateStubsReleaseKotlin"
+    ":hilt:hilt-navigation-compose:kaptGenerateStubsReleaseKotlin",
+
+    // https://github.com/gradle/gradle/issues/17262
+    ":doclava:compileJava",
+    ":doclava:processResources",
+    ":doclava:jar"
 )
 
 // Additional tasks that are expected to be temporarily out-of-date after running once
@@ -169,22 +174,53 @@
     "lintVitalRelease",
 )
 
-class TaskUpToDateValidator {
+class TaskUpToDateValidator :
+    BuildService<TaskUpToDateValidator.Parameters>, OperationCompletionListener {
+    open class Parameters : BuildServiceParameters
+    override fun getParameters(): Parameters {
+        return Parameters()
+    }
+
+    override fun onFinish(event: FinishEvent) {
+        val result = event.result
+        if (result is TaskExecutionResult) {
+            val name = event.descriptor.name
+            val executionReasons = result.executionReasons
+            if (executionReasons.isNullOrEmpty()) {
+                // empty list means task was actually up-to-date, see docs for
+                // TaskExecutionResult.executionReasons
+                // null list means the task already failed, so we'll skip emitting our error
+                return
+            }
+            if (!isAllowedToRerunTask(name)) {
+                throw GradleException(
+                    "Ran two consecutive builds of the same tasks, and in the " +
+                        "second build, observed:\n" +
+                        "task $name not UP-TO-DATE. It was out-of-date because:\n" +
+                        "${result.executionReasons}"
+                )
+            }
+        }
+    }
+
     companion object {
-
-        private val BUILD_START_TIME_KEY = "taskUpToDateValidatorSetupTime"
-
-        private fun shouldRecord(project: Project): Boolean {
-            return project.hasProperty(RECORD_FLAG_NAME)
-        }
-
         private fun shouldValidate(project: Project): Boolean {
-            return project.hasProperty(DISALLOW_TASK_EXECUTION_FLAG_NAME)
+            return project.providers.gradleProperty(DISALLOW_TASK_EXECUTION_FLAG_NAME)
+                .forUseAtConfigurationTime().isPresent()
         }
 
-        private fun isAllowedToRerunTask(task: Task): Boolean {
-            return ALLOW_RERUNNING_TASKS.contains(task.name) ||
-                ALLOW_RERUNNING_TASKS.contains(task.path)
+        private fun isAllowedToRerunTask(taskPath: String): Boolean {
+            if (ALLOW_RERUNNING_TASKS.contains(taskPath)) {
+                return true
+            }
+            val colonIndex = taskPath.lastIndexOf(":")
+            if (colonIndex >= 0) {
+                val taskName = taskPath.substring(colonIndex + 1)
+                if (ALLOW_RERUNNING_TASKS.contains(taskName)) {
+                    return true
+                }
+            }
+            return false
         }
 
         private fun shouldTryRerunningTask(task: Task): Boolean {
@@ -194,168 +230,25 @@
                 )
         }
 
-        private fun recordBuildStartTime(rootProject: Project) {
-            rootProject.extra.set(BUILD_START_TIME_KEY, Date())
-        }
+        fun setup(rootProject: Project, registry: BuildEventsListenerRegistry) {
+            if (!shouldValidate(rootProject)) {
+                return
+            }
+            // create listener for validating that any task that reran was expected to rerun
+            val validatorProvider = rootProject.getGradle().getSharedServices()
+                .registerIfAbsent(
+                    "TaskUpToDateValidator",
+                    TaskUpToDateValidator::class.java,
+                    { _ -> }
+                )
+            registry.onTaskCompletion(validatorProvider)
 
-        private fun getBuildStartTime(project: Project): Date {
-            return project.rootProject.extra.get(BUILD_START_TIME_KEY) as Date
-        }
-
-        fun setup(rootProject: Project) {
-            recordBuildStartTime(rootProject)
-            val taskGraph = rootProject.gradle.taskGraph
-            if (shouldValidate(rootProject)) {
-                taskGraph.beforeTask { task ->
-                    if (!shouldTryRerunningTask(task)) {
-                        task.enabled = false
-                    }
+            // skip rerunning tasks that are known to be unnecessary to rerun
+            rootProject.allprojects { subproject ->
+                subproject.tasks.configureEach { task ->
+                    task.onlyIf { shouldTryRerunningTask(task) }
                 }
             }
-            if (shouldRecord(rootProject) || shouldValidate(rootProject)) {
-                taskGraph.afterTask { task ->
-                    // In the second build, make sure that the task didn't rerun
-                    if (shouldValidate(rootProject)) {
-                        if (task.didWork) {
-                            if (!isAllowedToRerunTask(task)) {
-                                val message = "Ran two consecutive builds of the same tasks," +
-                                    " and in the second build, observed $task to be not " +
-                                    " UP-TO-DATE. This indicates that $task does not declare" +
-                                    " inputs and/or outputs correctly.\n" +
-                                    tryToExplainTaskExecution(task, taskGraph)
-                                throw GradleException(message)
-                            }
-                        }
-                    }
-                    // In the first build, record the task's inputs so that if they change in
-                    // the second build then we can compare.
-                    // In the second build, also record the task's inputs because we recorded
-                    // them in the first build, and we want the two builds to be as similar as
-                    // possible
-                    if (shouldTryRerunningTask(task) && !isAllowedToRerunTask(task)) {
-                        recordTaskInputs(task)
-                    }
-                }
-            }
-        }
-
-        fun recordTaskInputs(task: Task) {
-            val text = task.inputs.files.files.joinToString("\n")
-            val destFile = getTaskInputListPath(task)
-            destFile.parentFile.mkdirs()
-            destFile.writeText(text)
-        }
-
-        fun getTaskInputListPath(task: Task): File {
-            return File(getTasksInputListPath(task.project), task.name)
-        }
-
-        fun getTasksInputListPath(project: Project): File {
-            return File(project.buildDir, "TaskUpToDateValidator/inputs")
-        }
-
-        fun getPreviousTaskExecutionCompletionTimestamp(task: Task): Date {
-            // we're already saving the inputs of the task into a file,
-            // so we can check the timestamp of that file to know when the task last reran
-            val inputsFile = getTaskInputListPath(task)
-            return Date(inputsFile.lastModified())
-        }
-
-        fun checkForChangingSetOfInputs(task: Task): String {
-            val previousInputsFile = getTaskInputListPath(task)
-            val previousInputs = previousInputsFile.readLines()
-            val currentInputs = task.inputs.files.files.map { f -> f.toString() }
-            val addedInputs = currentInputs.minus(previousInputs)
-            val removedInputs = previousInputs.minus(currentInputs)
-            val addedMessage = if (addedInputs.size > 0) {
-                "Added these " + addedInputs.size + " inputs: " +
-                    addedInputs.joinToString("\n") + "\n"
-            } else {
-                ""
-            }
-            val removedMessage = if (removedInputs.size > 0) {
-                "Removed these " + removedInputs.size + " inputs: " +
-                    removedInputs.joinToString("\n") + "\n"
-            } else {
-                ""
-            }
-            return addedMessage + removedMessage
-        }
-
-        fun tryToExplainTaskExecution(task: Task, taskGraph: TaskExecutionGraph): String {
-            val numOutputFiles = task.outputs.files.files.size
-            val outputsMessage = if (numOutputFiles > 0) {
-                task.path + " declares " + numOutputFiles + " output files. This seems fine.\n"
-            } else {
-                task.path + " declares " + numOutputFiles + " output files. This is probably " +
-                    "an error.\n"
-            }
-
-            val inputFiles = task.inputs.files.files
-            var lastModifiedFile: File? = null
-            var lastModifiedWhen = Date(0)
-            for (inputFile in inputFiles) {
-                val modifiedWhen = Date(inputFile.lastModified())
-                if (modifiedWhen.compareTo(lastModifiedWhen) > 0) {
-                    lastModifiedFile = inputFile
-                    lastModifiedWhen = modifiedWhen
-                }
-            }
-
-            val inputSetModifiedMessage = checkForChangingSetOfInputs(task)
-            val inputsMessage = if (inputSetModifiedMessage != "") {
-                inputSetModifiedMessage
-            } else {
-                if (lastModifiedFile != null) {
-                    task.path + " declares " + inputFiles.size + " input files. The " +
-                        "last modified input file is\n" + lastModifiedFile + "\nmodified at " +
-                        lastModifiedWhen + " (the previous execution of this task completed at " +
-                        getPreviousTaskExecutionCompletionTimestamp(task) + " and this build " +
-                        "started at about " + getBuildStartTime(task.project) + "). " +
-                        tryToExplainFileModification(lastModifiedFile, taskGraph)
-                } else {
-                    task.path + " declares " + inputFiles.size + " input files.\n"
-                }
-            }
-
-            val reproductionMessage = "\nTo reproduce this error you can try running " +
-                "`./gradlew ${task.path} -P$RECORD_FLAG_NAME`\n"
-            val readLogsMessage = "\nYou can check why Gradle executed ${task.path} by " +
-                "passing the '--info' flag to Gradle and then searching stdout for output " +
-                "generated immediately before the task began to execute.\n" +
-                "Our best guess for the reason that ${task.path} executed is below.\n"
-            return readLogsMessage + outputsMessage + inputsMessage + reproductionMessage
-        }
-
-        fun getTaskDeclaringFile(file: File, taskGraph: TaskExecutionGraph): Task? {
-            for (task in taskGraph.allTasks) {
-                if (task.outputs.files.files.contains(file)) {
-                    return task
-                }
-            }
-            return null
-        }
-
-        fun tryToExplainFileModification(file: File, taskGraph: TaskExecutionGraph): String {
-            // Find the task declaring this file as an output,
-            // or the task declaring one of its parent dirs as an output
-            var createdByTask: Task? = null
-            var declaredFile: File? = file
-            while (createdByTask == null && declaredFile != null) {
-                createdByTask = getTaskDeclaringFile(declaredFile, taskGraph)
-                declaredFile = declaredFile.parentFile
-            }
-            if (createdByTask == null) {
-                return "This file is not declared as the output of any task in this build."
-            }
-            if (isAllowedToRerunTask(createdByTask)) {
-                return "This file is declared as an output of " + createdByTask +
-                    ", which is a task that is not yet validated by the TaskUpToDateValidator"
-            } else {
-                return "This file is decared as an output of " + createdByTask +
-                    ", which is a task that is validated by the TaskUpToDateValidator " +
-                    "(and therefore must not have been out-of-date during this build)"
-            }
         }
     }
 }
diff --git a/busytown/androidx_incremental.sh b/busytown/androidx_incremental.sh
index 2c3383c1..f58af59 100755
--- a/busytown/androidx_incremental.sh
+++ b/busytown/androidx_incremental.sh
@@ -33,7 +33,7 @@
 function zipKotlinMetadata() {
   zipFile=kotlinMetadata.zip
   echo "zipping kotlin metadata"
-  (cd $OUT_DIR && find -name "*kotlin_metadata" | xargs zip "$DIST_DIR/$zipFile")
+  (cd $OUT_DIR && find -name "*kotlin_module" | xargs zip "$DIST_DIR/$zipFile")
   echo done zipping kotlin metadata
 }
 
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index d7ee6fb..09c1751 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
+
 import androidx.build.BundleInsideHelper
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -49,29 +48,29 @@
     // Classes and types that are only needed at runtime
     implementation(project(":lifecycle:lifecycle-livedata-ktx"))
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
-    implementation(KOTLIN_COROUTINES_GUAVA)
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinCoroutinesGuava)
+    implementation(libs.kotlinStdlib)
 
     // Since we jarjar CameraPipe, include the transitive dependencies as implementation
     implementation(CAMERA_PIPE_DEPS.API)
     implementation(CAMERA_PIPE_DEPS.IMPLEMENTATION)
 
-    kapt(DAGGER_COMPILER)
+    kapt(libs.dagger)
 
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(project(":camera:camera-camera2-pipe-testing"))
     testImplementation(project(":internal-testutils-truth"))
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.truth)
     androidTestImplementation(project(":annotation:annotation-experimental"))
     androidTestImplementation(project(":camera:camera-lifecycle"))
     androidTestImplementation(project(":concurrent:concurrent-futures-ktx"))
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index 8645cc2..f4f27b5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -140,7 +140,29 @@
 
     object DefaultSessionOptionsUnpacker : SessionConfig.OptionUnpacker {
         override fun unpack(config: UseCaseConfig<*>, builder: SessionConfig.Builder) {
-            // Unused.
+            val defaultSessionConfig = config.getDefaultSessionConfig( /*valueIfMissing=*/null)
+
+            var implOptions: Config = OptionsBundle.emptyBundle()
+            var templateType = SessionConfig.defaultEmptySessionConfig().templateType
+
+            // Apply/extract defaults from session config
+            if (defaultSessionConfig != null) {
+                templateType = defaultSessionConfig.templateType
+                builder.addAllDeviceStateCallbacks(defaultSessionConfig.deviceStateCallbacks)
+                builder.addAllSessionStateCallbacks(defaultSessionConfig.sessionStateCallbacks)
+                builder.addAllRepeatingCameraCaptureCallbacks(
+                    defaultSessionConfig.repeatingCameraCaptureCallbacks
+                )
+                implOptions = defaultSessionConfig.implementationOptions
+            }
+
+            // Set any additional implementation options
+            builder.setImplementationOptions(implOptions)
+
+            // Set the template type from default session config
+            builder.setTemplateType(templateType)
+
+            // TODO: Add Camera2 options and callbacks
         }
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt
index 8c846fc..78a488a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.camera.camera2.pipe.integration.adapter
 
+import android.hardware.camera2.CameraCaptureSession
 import android.hardware.camera2.CameraDevice
 import android.os.Build
 import android.view.Surface
@@ -24,6 +25,7 @@
 import androidx.camera.core.impl.CaptureConfig
 import androidx.camera.core.impl.ImageOutputConfig
 import androidx.camera.core.impl.MutableOptionsBundle
+import androidx.camera.core.impl.SessionConfig
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -59,6 +61,60 @@
         config.assertEquals(useCaseConfig.defaultCaptureConfig)
     }
 
+    @Test
+    fun shouldApplySessionConfig_whenDefaultConfigSet() {
+        // Arrange
+        val defaultSessionCaptureConfig = SessionConfig.Builder()
+            .apply {
+                setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
+                addImplementationOptions(
+                    MutableOptionsBundle.create()
+                        .apply {
+                            insertOption(
+                                ImageOutputConfig.OPTION_TARGET_ROTATION,
+                                Surface.ROTATION_180
+                            )
+                        }
+                )
+                addDeviceStateCallback(object : CameraDevice.StateCallback() {
+                    override fun onOpened(camera: CameraDevice) {
+                        // unused
+                    }
+
+                    override fun onDisconnected(camera: CameraDevice) {
+                        // unused
+                    }
+
+                    override fun onError(camera: CameraDevice, error: Int) {
+                        // unused
+                    }
+                })
+                addSessionStateCallback(object : CameraCaptureSession.StateCallback() {
+                    override fun onConfigured(session: CameraCaptureSession) {
+                        // unused
+                    }
+
+                    override fun onConfigureFailed(session: CameraCaptureSession) {
+                        // unused
+                    }
+                })
+                addRepeatingCameraCaptureCallback(object : CameraCaptureCallback() {})
+            }
+            .build()
+
+        val useCaseConfig = ImageCapture.Builder()
+            .setDefaultSessionConfig(defaultSessionCaptureConfig)
+            .useCaseConfig
+        val builder = SessionConfig.Builder()
+
+        // Act
+        CameraUseCaseAdapter.DefaultSessionOptionsUnpacker.unpack(useCaseConfig, builder)
+
+        // Assert
+        val config = builder.build()
+        config.assertEquals(useCaseConfig.defaultSessionConfig)
+    }
+
     private fun CaptureConfig.assertEquals(other: CaptureConfig) {
         assertThat(templateType).isEqualTo(other.templateType)
         assertThat(isUseRepeatingSurface).isEqualTo(other.isUseRepeatingSurface)
@@ -80,4 +136,21 @@
             assertThat(tagBundle.getTag(key)).isEqualTo(other.tagBundle.getTag(key))
         }
     }
+
+    private fun SessionConfig.assertEquals(other: SessionConfig) {
+        assertThat(templateType).isEqualTo(other.templateType)
+        // Implementation options
+        assertThat(implementationOptions.listOptions())
+            .isEqualTo(other.implementationOptions.listOptions())
+        implementationOptions.listOptions().forEach { option ->
+            assertThat(implementationOptions.retrieveOption(option)).isEqualTo(
+                other.implementationOptions.retrieveOption(option)
+            )
+        }
+
+        // Verify callbacks
+        assertThat(deviceStateCallbacks).isEqualTo(other.deviceStateCallbacks)
+        assertThat(sessionStateCallbacks).isEqualTo(other.sessionStateCallbacks)
+        assertThat(repeatingCameraCaptureCallbacks).isEqualTo(other.repeatingCameraCaptureCallbacks)
+    }
 }
diff --git a/camera/camera-camera2-pipe-testing/build.gradle b/camera/camera-camera2-pipe-testing/build.gradle
index c340844..189f0a1 100644
--- a/camera/camera-camera2-pipe-testing/build.gradle
+++ b/camera/camera-camera2-pipe-testing/build.gradle
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
+
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 import androidx.build.RunApiTasks
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -33,16 +32,16 @@
     api("androidx.annotation:annotation:1.1.0")
 
     // Classes and types that are only needed at runtime
-    implementation(KOTLIN_STDLIB)
-    implementation(KOTLIN_COROUTINES_GUAVA)
+    implementation(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesGuava)
     implementation(project(":camera:camera-camera2-pipe"))
 
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(TRUTH)
-    testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.truth)
+    testImplementation(libs.kotlinCoroutinesTest)
 }
 
 android {
diff --git a/camera/camera-camera2-pipe/build.gradle b/camera/camera-camera2-pipe/build.gradle
index 50d5b87..bbc9516 100644
--- a/camera/camera-camera2-pipe/build.gradle
+++ b/camera/camera-camera2-pipe/build.gradle
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
+
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 import androidx.build.RunApiTasks
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -38,22 +37,22 @@
     api(CAMERA_PIPE_DEPS.API)
     implementation(CAMERA_PIPE_DEPS.IMPLEMENTATION)
 
-    kapt(DAGGER_COMPILER)
+    kapt(libs.dagger)
 
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(project(":camera:camera-camera2-pipe-testing"))
     testImplementation(project(":internal-testutils-truth"))
 
-    kaptTest(DAGGER_COMPILER)
+    kaptTest(libs.dagger)
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.truth)
 }
 
 android {
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 55981b2..3052687 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -18,8 +18,6 @@
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -31,36 +29,36 @@
     api("androidx.annotation:annotation:1.2.0")
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
-    implementation(GUAVA_LISTENABLE_FUTURE)
-    implementation(AUTO_VALUE_ANNOTATIONS)
+    implementation(libs.guavaListenableFuture)
+    implementation(libs.autoValueAnnotations)
 
-    annotationProcessor(AUTO_VALUE)
+    annotationProcessor(libs.autoValue)
 
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(MOCKITO_CORE)
-    testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.mockitoCore)
+    testImplementation(libs.kotlinCoroutinesTest)
     testImplementation("androidx.annotation:annotation-experimental:1.1.0")
     testImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
     testImplementation(project(":camera:camera-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0")
     androidTestImplementation(project(":camera:camera-testing"))
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
     androidTestImplementation("androidx.annotation:annotation-experimental:1.1.0")
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation("org.jetbrains.kotlinx:atomicfu:0.13.1")
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
index e84383d..46ab8cd 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
@@ -41,12 +41,10 @@
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.OptIn;
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
 import androidx.camera.camera2.internal.util.SemaphoreReleasingCamera2Callbacks;
 import androidx.camera.camera2.interop.Camera2Interop;
 import androidx.camera.core.CameraSelector;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.ExposureState;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.impl.CameraControlInternal;
@@ -94,7 +92,6 @@
  */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-@OptIn(markerClass = ExperimentalExposureCompensation.class)
 public class ExposureDeviceTest {
 
     @CameraSelector.LensFacing
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
index de30395..6417153 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
@@ -43,7 +43,6 @@
 import androidx.camera.camera2.interop.Camera2CameraControl;
 import androidx.camera.camera2.interop.CaptureRequestOptions;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.FocusMeteringResult;
 import androidx.camera.core.ImageCapture;
@@ -420,7 +419,6 @@
 
     @NonNull
     @Override
-    @ExperimentalExposureCompensation
     public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
         if (!isControlInUse()) {
             return Futures.immediateFailedFuture(
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
index 66925a2..c6bd795 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
@@ -31,7 +31,6 @@
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraState;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.ExposureState;
 import androidx.camera.core.Logger;
 import androidx.camera.core.ZoomState;
@@ -294,7 +293,6 @@
 
     @NonNull
     @Override
-    @ExperimentalExposureCompensation
     public ExposureState getExposureState() {
         synchronized (mLock) {
             if (mCamera2CameraControlImpl == null) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
index 8b87d66..bf9b6e9 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
@@ -23,7 +23,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.OptIn;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
 import androidx.camera.camera2.internal.annotation.CameraExecutor;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
@@ -53,7 +52,6 @@
  * The task will fails with {@link CameraControl.OperationCanceledException} if the camera is
  * closed.
  */
-@OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
 public class ExposureControl {
 
     private static final int DEFAULT_EXPOSURE_COMPENSATION = 0;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java
index bf44fdf..fb4a514 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java
@@ -23,13 +23,11 @@
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.ExposureState;
 
 /**
  * An implementation of {@link ExposureState} where the values can be set.
  */
-@ExperimentalExposureCompensation
 class ExposureStateImpl implements ExposureState {
 
     private final Object mLock = new Object();
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
index 58f8e22..1909102 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
@@ -30,10 +30,8 @@
 import android.util.Range;
 import android.util.Rational;
 
-import androidx.annotation.OptIn;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.core.CameraControl;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.impl.CameraControlInternal;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.test.core.app.ApplicationProvider;
@@ -57,7 +55,6 @@
 @RunWith(RobolectricTestRunner.class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 @DoNotInstrument
-@OptIn(markerClass = ExperimentalExposureCompensation.class)
 public class ExposureControlTest {
 
     private static final String CAMERA0_ID = "0";
diff --git a/camera/camera-core/api/current.ignore b/camera/camera-core/api/current.ignore
index 2f97885..17d923e 100644
--- a/camera/camera-core/api/current.ignore
+++ b/camera/camera-core/api/current.ignore
@@ -1,5 +1,9 @@
 // Baseline format: 1.0
+AddedAbstractMethod: androidx.camera.core.CameraControl#setExposureCompensationIndex(int):
+    Added method androidx.camera.core.CameraControl.setExposureCompensationIndex(int)
 AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraSelector():
     Added method androidx.camera.core.CameraInfo.getCameraSelector()
 AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraState():
     Added method androidx.camera.core.CameraInfo.getCameraState()
+AddedAbstractMethod: androidx.camera.core.CameraInfo#getExposureState():
+    Added method androidx.camera.core.CameraInfo.getExposureState()
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index 4519564..46c69c1 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -14,6 +14,7 @@
   public interface CameraControl {
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
@@ -29,6 +30,7 @@
   public interface CameraInfo {
     method public androidx.camera.core.CameraSelector getCameraSelector();
     method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
+    method public androidx.camera.core.ExposureState getExposureState();
     method public int getSensorRotationDegrees();
     method public int getSensorRotationDegrees(int);
     method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
@@ -122,6 +124,13 @@
     ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
   }
 
+  public interface ExposureState {
+    method public int getExposureCompensationIndex();
+    method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
+    method public android.util.Rational getExposureCompensationStep();
+    method public boolean isExposureCompensationSupported();
+  }
+
   public interface ExtendableBuilder<T> {
     method public T build();
   }
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index ea9906c..daa3256 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -14,7 +14,7 @@
   public interface CameraControl {
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
-    method @androidx.camera.core.ExperimentalExposureCompensation public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
@@ -30,7 +30,7 @@
   public interface CameraInfo {
     method public androidx.camera.core.CameraSelector getCameraSelector();
     method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
-    method @androidx.camera.core.ExperimentalExposureCompensation public androidx.camera.core.ExposureState getExposureState();
+    method public androidx.camera.core.ExposureState getExposureState();
     method public int getSensorRotationDegrees();
     method public int getSensorRotationDegrees(int);
     method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
@@ -135,9 +135,6 @@
   @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCustomizableThreads {
   }
 
-  @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalExposureCompensation {
-  }
-
   @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalGetImage {
   }
 
@@ -147,7 +144,7 @@
   @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalUseCaseGroup {
   }
 
-  @androidx.camera.core.ExperimentalExposureCompensation public interface ExposureState {
+  public interface ExposureState {
     method public int getExposureCompensationIndex();
     method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
     method public android.util.Rational getExposureCompensationStep();
diff --git a/camera/camera-core/api/restricted_current.ignore b/camera/camera-core/api/restricted_current.ignore
index 2f97885..17d923e 100644
--- a/camera/camera-core/api/restricted_current.ignore
+++ b/camera/camera-core/api/restricted_current.ignore
@@ -1,5 +1,9 @@
 // Baseline format: 1.0
+AddedAbstractMethod: androidx.camera.core.CameraControl#setExposureCompensationIndex(int):
+    Added method androidx.camera.core.CameraControl.setExposureCompensationIndex(int)
 AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraSelector():
     Added method androidx.camera.core.CameraInfo.getCameraSelector()
 AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraState():
     Added method androidx.camera.core.CameraInfo.getCameraState()
+AddedAbstractMethod: androidx.camera.core.CameraInfo#getExposureState():
+    Added method androidx.camera.core.CameraInfo.getExposureState()
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index 4519564..46c69c1 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -14,6 +14,7 @@
   public interface CameraControl {
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
@@ -29,6 +30,7 @@
   public interface CameraInfo {
     method public androidx.camera.core.CameraSelector getCameraSelector();
     method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
+    method public androidx.camera.core.ExposureState getExposureState();
     method public int getSensorRotationDegrees();
     method public int getSensorRotationDegrees(int);
     method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
@@ -122,6 +124,13 @@
     ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
   }
 
+  public interface ExposureState {
+    method public int getExposureCompensationIndex();
+    method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
+    method public android.util.Rational getExposureCompensationStep();
+    method public boolean isExposureCompensationSupported();
+  }
+
   public interface ExtendableBuilder<T> {
     method public T build();
   }
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index aa1cb74..10d5397 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -18,8 +18,6 @@
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -29,39 +27,39 @@
 dependencies {
     api("androidx.annotation:annotation:1.2.0")
     api("androidx.lifecycle:lifecycle-livedata:2.1.0")
-    api(GUAVA_LISTENABLE_FUTURE)
+    api(libs.guavaListenableFuture)
     api("androidx.annotation:annotation-experimental:1.1.0")
-    api(KOTLIN_STDLIB) // Added for annotation-experimental
+    api(libs.kotlinStdlib) // Added for annotation-experimental
     implementation("androidx.exifinterface:exifinterface:1.0.0")
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.lifecycle:lifecycle-common:2.1.0")
-    implementation(AUTO_VALUE_ANNOTATIONS)
+    implementation(libs.autoValueAnnotations)
 
-    annotationProcessor(AUTO_VALUE)
+    annotationProcessor(libs.autoValue)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.mockitoCore)
     testImplementation(project(":camera:camera-testing"), {
         exclude group: "androidx.camera", module: "camera-core"
     })
     testImplementation("androidx.exifinterface:exifinterface:1.0.0")
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(project(":camera:camera-testing"))
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
     androidTestImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     androidTestImplementation(project(":internal-testutils-truth"))
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
index e9df24a..9fa6afc 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
@@ -176,7 +176,6 @@
      * </ul>
      */
     @NonNull
-    @ExperimentalExposureCompensation
     ListenableFuture<Integer> setExposureCompensationIndex(int value);
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
index fa3d911..00f4c40 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
@@ -138,7 +138,6 @@
      * <p>The {@link ExposureState} contains the current exposure related information.
      */
     @NonNull
-    @ExperimentalExposureCompensation
     ExposureState getExposureState();
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalExposureCompensation.java b/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalExposureCompensation.java
deleted file mode 100644
index 6e22ab7..0000000
--- a/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalExposureCompensation.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2020 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.camera.core;
-
-
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-import androidx.annotation.RequiresOptIn;
-
-import java.lang.annotation.Retention;
-
-/**
- * Denotes that the annotated method uses the experimental ExposureCompensation APIs that can
- * control the exposure compensation of the camera.
- *
- * <p>The feature allow the user to control the exposure compensation of the camera, it includes a
- * setter in {@link androidx.camera.core.CameraControl} and a getter in
- * {@link androidx.camera.core.CameraInfo}.
- */
-@Retention(CLASS)
-@RequiresOptIn
-public @interface ExperimentalExposureCompensation {
-}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java b/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java
index 919f708..7979a74 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java
@@ -26,7 +26,6 @@
  *
  * <p>Applications can retrieve an instance via {@link CameraInfo#getExposureState()}.
  */
-@ExperimentalExposureCompensation
 public interface ExposureState {
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
index d5354ed..ac35a05 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
@@ -22,7 +22,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.CameraControl;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.FocusMeteringResult;
 import androidx.camera.core.ImageCapture.FlashMode;
@@ -82,7 +81,6 @@
      */
     @NonNull
     @Override
-    @ExperimentalExposureCompensation
     ListenableFuture<Integer> setExposureCompensationIndex(int exposure);
 
     /**
@@ -157,7 +155,6 @@
 
         @NonNull
         @Override
-        @ExperimentalExposureCompensation
         public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
             return Futures.immediateFuture(0);
         }
diff --git a/camera/camera-extensions-stub/build.gradle b/camera/camera-extensions-stub/build.gradle
index a5511a1..6d234f3 100644
--- a/camera/camera-extensions-stub/build.gradle
+++ b/camera/camera-extensions-stub/build.gradle
@@ -12,12 +12,12 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryVersions
-import androidx.build.LibraryGroups
-import androidx.build.Publish
 
-plugins {
+ import androidx.build.LibraryGroups
+ import androidx.build.LibraryVersions
+ import androidx.build.Publish
+
+ plugins {
     id("AndroidXPlugin")
     id("com.android.library")
 }
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 9740be7..c9cfa28 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryVersions
+
 import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 import androidx.build.Publish
 import androidx.build.RunApiTasks
 
@@ -27,32 +27,32 @@
 }
 
 dependencies {
-    api(GUAVA_LISTENABLE_FUTURE)
+    api(libs.guavaListenableFuture)
     api(project(":camera:camera-core"))
     implementation(project(":camera:camera-camera2"))
     implementation("androidx.core:core:1.0.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
-    implementation(AUTO_VALUE_ANNOTATIONS)
-    annotationProcessor(AUTO_VALUE)
+    implementation(libs.autoValueAnnotations)
+    annotationProcessor(libs.autoValue)
 
     compileOnly(project(":camera:camera-extensions-stub"))
 
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(TRUTH)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.truth)
     testImplementation(project(":camera:camera-testing"))
     testImplementation(project(":camera:camera-extensions-stub"))
     // To use the extensions-stub for testing directly.
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(libs.truth)
     androidTestImplementation(project(":camera:camera-lifecycle"))
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(project(":internal-testutils-truth"))
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
index 4d9c5fa..48ec5ce 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
@@ -51,7 +51,6 @@
 import androidx.camera.core.impl.PreviewConfig;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
-import androidx.camera.extensions.ExtensionsManager.EffectMode;
 import androidx.camera.extensions.util.ExtensionsTestUtil;
 import androidx.camera.lifecycle.ProcessCameraProvider;
 import androidx.camera.testing.CameraUtil;
@@ -93,7 +92,7 @@
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
-    private final EffectMode mEffectMode;
+    private final ExtensionsManager.EffectMode mEffectMode;
     @ExtensionMode.Mode
     private final int mExtensionMode;
     @CameraSelector.LensFacing
@@ -104,7 +103,8 @@
     private CameraSelector mBaseCameraSelector;
     private CameraSelector mExtensionsCameraSelector;
 
-    public ExtensionTest(EffectMode effectMode, @CameraSelector.LensFacing int lensFacing) {
+    public ExtensionTest(ExtensionsManager.EffectMode effectMode,
+            @CameraSelector.LensFacing int lensFacing) {
         mEffectMode = effectMode;
         mExtensionMode = ExtensionsTestUtil.effectModeToExtensionMode(mEffectMode);
         mLensFacing = lensFacing;
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java
index 25959ab..3f8c741 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java
@@ -39,7 +39,6 @@
 import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
 import androidx.camera.extensions.ExtensionsErrorListener.ExtensionsErrorCode;
-import androidx.camera.extensions.ExtensionsManager.EffectMode;
 import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.PreviewExtenderImpl;
 import androidx.camera.extensions.util.ExtensionsTestUtil;
@@ -91,7 +90,7 @@
 
     private ExtensionsInfo mExtensionsInfo;
     private CameraSelector mCameraSelector;
-    private EffectMode mEffectMode;
+    private ExtensionsManager.EffectMode mEffectMode;
     @ExtensionMode.Mode
     private int mExtensionMode;
     @CameraSelector.LensFacing
@@ -111,7 +110,7 @@
         }
     };
 
-    public ExtensionsErrorListenerTest(EffectMode effectMode,
+    public ExtensionsErrorListenerTest(ExtensionsManager.EffectMode effectMode,
             @CameraSelector.LensFacing int lensFacing) {
         mEffectMode = effectMode;
         mExtensionMode = effectModeToExtensionMode(effectMode);
@@ -165,8 +164,8 @@
 
         ImageCapture imageCapture = ExtensionsTestUtil.createImageCaptureWithEffect(mEffectMode,
                 mLensFacing);
-        Preview noEffectPreview = ExtensionsTestUtil.createPreviewWithEffect(EffectMode.NORMAL,
-                mLensFacing);
+        Preview noEffectPreview = ExtensionsTestUtil.createPreviewWithEffect(
+                ExtensionsManager.EffectMode.NORMAL, mLensFacing);
         mErrorCode.set(null);
 
         mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, imageCapture,
@@ -197,8 +196,8 @@
     public void receiveErrorCode_whenOnlyEnablePreview_ByExtenderAPI() throws InterruptedException {
         ExtensionsManager.setExtensionsErrorListener(mExtensionsErrorListener);
 
-        ImageCapture noEffectImageCapture =
-                ExtensionsTestUtil.createImageCaptureWithEffect(EffectMode.NORMAL, mLensFacing);
+        ImageCapture noEffectImageCapture = ExtensionsTestUtil.createImageCaptureWithEffect(
+                ExtensionsManager.EffectMode.NORMAL, mLensFacing);
         Preview preview = ExtensionsTestUtil.createPreviewWithEffect(mEffectMode, mLensFacing);
         mErrorCode.set(null);
 
@@ -248,16 +247,16 @@
     @Test
     public void receiveErrorCode_whenEnableMismatchedImageCapturePreview_ByExtenderAPI()
             throws InterruptedException, CameraAccessException, CameraInfoUnavailableException {
-        EffectMode mismatchedEffectMode;
+        ExtensionsManager.EffectMode mismatchedEffectMode;
 
-        if (mEffectMode != EffectMode.BOKEH) {
-            assumeTrue(ExtensionsManager.isExtensionAvailable(EffectMode.BOKEH,
+        if (mEffectMode != ExtensionsManager.EffectMode.BOKEH) {
+            assumeTrue(ExtensionsManager.isExtensionAvailable(ExtensionsManager.EffectMode.BOKEH,
                     mLensFacing));
-            mismatchedEffectMode = EffectMode.BOKEH;
+            mismatchedEffectMode = ExtensionsManager.EffectMode.BOKEH;
         } else {
-            assumeTrue(ExtensionsManager.isExtensionAvailable(EffectMode.HDR,
+            assumeTrue(ExtensionsManager.isExtensionAvailable(ExtensionsManager.EffectMode.HDR,
                     mLensFacing));
-            mismatchedEffectMode = EffectMode.HDR;
+            mismatchedEffectMode = ExtensionsManager.EffectMode.HDR;
         }
 
         assumeTrue(canSupportImageCaptureTogetherWithPreview(mEffectMode, mismatchedEffectMode));
@@ -340,7 +339,8 @@
     }
 
     private boolean canSupportImageCaptureTogetherWithPreview(
-            @NonNull EffectMode imageCaptureEffectMode, @NonNull EffectMode previewEffectMode)
+            @NonNull ExtensionsManager.EffectMode imageCaptureEffectMode,
+            @NonNull ExtensionsManager.EffectMode previewEffectMode)
             throws CameraAccessException, CameraInfoUnavailableException {
 
         CameraUseCaseAdapter camera = CameraUtil.createCameraUseCaseAdapter(mContext,
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsInfoTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsInfoTest.kt
index 4e5daf7..e1e435d 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsInfoTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsInfoTest.kt
@@ -21,7 +21,6 @@
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraSelector.LensFacing
 import androidx.camera.extensions.ExtensionMode.Mode
-import androidx.camera.extensions.ExtensionsManager.EffectMode
 import androidx.camera.extensions.util.ExtensionsTestUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -54,7 +53,7 @@
 
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
 
-    private val effectMode: EffectMode =
+    private val effectMode: ExtensionsManager.EffectMode =
         ExtensionsTestUtil.extensionModeToEffectMode(extensionMode)
 
     private lateinit var extensionsInfo: ExtensionsInfo
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
index 458720a..4e98ef6 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
@@ -27,29 +27,15 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.Preview;
 import androidx.camera.core.impl.ImageCaptureConfig;
 import androidx.camera.core.impl.PreviewConfig;
-import androidx.camera.extensions.AutoImageCaptureExtender;
-import androidx.camera.extensions.AutoPreviewExtender;
-import androidx.camera.extensions.BeautyImageCaptureExtender;
-import androidx.camera.extensions.BeautyPreviewExtender;
-import androidx.camera.extensions.BokehImageCaptureExtender;
-import androidx.camera.extensions.BokehPreviewExtender;
 import androidx.camera.extensions.ExtensionMode;
 import androidx.camera.extensions.ExtensionsManager;
-import androidx.camera.extensions.ExtensionsManager.EffectMode;
 import androidx.camera.extensions.ExtensionsManager.ExtensionsAvailability;
-import androidx.camera.extensions.HdrImageCaptureExtender;
-import androidx.camera.extensions.HdrPreviewExtender;
-import androidx.camera.extensions.ImageCaptureExtender;
-import androidx.camera.extensions.NightImageCaptureExtender;
-import androidx.camera.extensions.NightPreviewExtender;
-import androidx.camera.extensions.PreviewExtender;
 import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
 import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
@@ -78,16 +64,16 @@
     @NonNull
     public static Collection<Object[]> getAllEffectLensFacingCombinations() {
         return Arrays.asList(new Object[][]{
-                {EffectMode.BOKEH, CameraSelector.LENS_FACING_FRONT},
-                {EffectMode.BOKEH, CameraSelector.LENS_FACING_BACK},
-                {EffectMode.HDR, CameraSelector.LENS_FACING_FRONT},
-                {EffectMode.HDR, CameraSelector.LENS_FACING_BACK},
-                {EffectMode.BEAUTY, CameraSelector.LENS_FACING_FRONT},
-                {EffectMode.BEAUTY, CameraSelector.LENS_FACING_BACK},
-                {EffectMode.NIGHT, CameraSelector.LENS_FACING_FRONT},
-                {EffectMode.NIGHT, CameraSelector.LENS_FACING_BACK},
-                {EffectMode.AUTO, CameraSelector.LENS_FACING_FRONT},
-                {EffectMode.AUTO, CameraSelector.LENS_FACING_BACK}
+                {ExtensionsManager.EffectMode.BOKEH, CameraSelector.LENS_FACING_FRONT},
+                {ExtensionsManager.EffectMode.BOKEH, CameraSelector.LENS_FACING_BACK},
+                {ExtensionsManager.EffectMode.HDR, CameraSelector.LENS_FACING_FRONT},
+                {ExtensionsManager.EffectMode.HDR, CameraSelector.LENS_FACING_BACK},
+                {ExtensionsManager.EffectMode.BEAUTY, CameraSelector.LENS_FACING_FRONT},
+                {ExtensionsManager.EffectMode.BEAUTY, CameraSelector.LENS_FACING_BACK},
+                {ExtensionsManager.EffectMode.NIGHT, CameraSelector.LENS_FACING_FRONT},
+                {ExtensionsManager.EffectMode.NIGHT, CameraSelector.LENS_FACING_BACK},
+                {ExtensionsManager.EffectMode.AUTO, CameraSelector.LENS_FACING_FRONT},
+                {ExtensionsManager.EffectMode.AUTO, CameraSelector.LENS_FACING_BACK}
         });
     }
 
@@ -129,7 +115,7 @@
     }
 
     @ExtensionMode.Mode
-    public static int effectModeToExtensionMode(@NonNull EffectMode effectMode) {
+    public static int effectModeToExtensionMode(@NonNull ExtensionsManager.EffectMode effectMode) {
         switch (effectMode) {
             case NORMAL:
                 return ExtensionMode.NONE;
@@ -147,27 +133,28 @@
         throw new IllegalArgumentException("Effect mode not found: " + effectMode);
     }
 
-    public static EffectMode extensionModeToEffectMode(@ExtensionMode.Mode int mode) {
+    public static ExtensionsManager.EffectMode extensionModeToEffectMode(
+            @ExtensionMode.Mode int mode) {
         switch (mode) {
             case ExtensionMode.NONE:
-                return EffectMode.NORMAL;
+                return ExtensionsManager.EffectMode.NORMAL;
             case ExtensionMode.BOKEH:
-                return EffectMode.BOKEH;
+                return ExtensionsManager.EffectMode.BOKEH;
             case ExtensionMode.HDR:
-                return EffectMode.HDR;
+                return ExtensionsManager.EffectMode.HDR;
             case ExtensionMode.NIGHT:
-                return EffectMode.NIGHT;
+                return ExtensionsManager.EffectMode.NIGHT;
             case ExtensionMode.BEAUTY:
-                return EffectMode.BEAUTY;
+                return ExtensionsManager.EffectMode.BEAUTY;
             case ExtensionMode.AUTO:
-                return EffectMode.AUTO;
+                return ExtensionsManager.EffectMode.AUTO;
         }
         throw new IllegalArgumentException("Extension mode not found: " + mode);
     }
 
     /**
-     * Creates an {@link ImageCapture.Builder} object for specific {@link EffectMode} and
-     * {@link CameraSelector.LensFacing}.
+     * Creates an {@link ImageCapture.Builder} object for specific
+     * {@link ExtensionsManager.EffectMode} and {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
      * @param lensFacing The lens facing for the created object.
@@ -175,32 +162,33 @@
      */
     @NonNull
     public static ImageCapture.Builder createImageCaptureConfigBuilderWithEffect(
-            @NonNull EffectMode effectMode, @CameraSelector.LensFacing int lensFacing) {
+            @NonNull ExtensionsManager.EffectMode effectMode,
+            @CameraSelector.LensFacing int lensFacing) {
         ImageCapture.Builder builder = new ImageCapture.Builder();
         CameraSelector selector =
                 new CameraSelector.Builder().requireLensFacing(lensFacing).build();
-        ImageCaptureExtender extender = null;
+        androidx.camera.extensions.ImageCaptureExtender extender = null;
 
         switch (effectMode) {
             case HDR:
-                extender = HdrImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.HdrImageCaptureExtender.create(builder);
                 break;
             case BOKEH:
-                extender = BokehImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.BokehImageCaptureExtender.create(builder);
                 break;
             case BEAUTY:
-                extender = BeautyImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.BeautyImageCaptureExtender.create(builder);
                 break;
             case NIGHT:
-                extender = NightImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.NightImageCaptureExtender.create(builder);
                 break;
             case AUTO:
-                extender = AutoImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.AutoImageCaptureExtender.create(builder);
                 break;
         }
 
         // Applies effect configs if it is not normal mode.
-        if (effectMode != EffectMode.NORMAL) {
+        if (effectMode != ExtensionsManager.EffectMode.NORMAL) {
             assertNotNull(extender);
             assertTrue(extender.isExtensionAvailable(selector));
             extender.enableExtension(selector);
@@ -210,41 +198,42 @@
     }
 
     /**
-     * Creates a {@link Preview.Builder} object for specific {@link EffectMode} and
-     * {@link CameraSelector.LensFacing}.
+     * Creates a {@link Preview.Builder} object for specific {@link ExtensionsManager.EffectMode}
+     * and {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
      * @param lensFacing The lens facing for the created object.
      * @return A {@link Preview.Builder} object.
      */
     @NonNull
-    public static Preview.Builder createPreviewBuilderWithEffect(@NonNull EffectMode effectMode,
+    public static Preview.Builder createPreviewBuilderWithEffect(
+            @NonNull ExtensionsManager.EffectMode effectMode,
             @CameraSelector.LensFacing int lensFacing) {
         Preview.Builder builder = new Preview.Builder();
         CameraSelector selector =
                 new CameraSelector.Builder().requireLensFacing(lensFacing).build();
-        PreviewExtender extender = null;
+        androidx.camera.extensions.PreviewExtender extender = null;
 
         switch (effectMode) {
             case HDR:
-                extender = HdrPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.HdrPreviewExtender.create(builder);
                 break;
             case BOKEH:
-                extender = BokehPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.BokehPreviewExtender.create(builder);
                 break;
             case BEAUTY:
-                extender = BeautyPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.BeautyPreviewExtender.create(builder);
                 break;
             case NIGHT:
-                extender = NightPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.NightPreviewExtender.create(builder);
                 break;
             case AUTO:
-                extender = AutoPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.AutoPreviewExtender.create(builder);
                 break;
         }
 
         // Applies effect configs if it is not normal mode.
-        if (effectMode != EffectMode.NORMAL) {
+        if (effectMode != ExtensionsManager.EffectMode.NORMAL) {
             assertNotNull(extender);
             assertTrue(extender.isExtensionAvailable(selector));
             extender.enableExtension(selector);
@@ -254,8 +243,8 @@
     }
 
     /**
-     * Creates an {@link ImageCaptureConfig} object for specific {@link EffectMode} and
-     * {@link CameraSelector.LensFacing}.
+     * Creates an {@link ImageCaptureConfig} object for specific
+     * {@link ExtensionsManager.EffectMode} and {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
      * @param lensFacing The lens facing for the created object.
@@ -263,14 +252,15 @@
      */
     @NonNull
     public static ImageCaptureConfig createImageCaptureConfigWithEffect(
-            @NonNull EffectMode effectMode, @CameraSelector.LensFacing int lensFacing) {
+            @NonNull ExtensionsManager.EffectMode effectMode,
+            @CameraSelector.LensFacing int lensFacing) {
         ImageCapture.Builder imageCaptureConfigBuilder =
                 createImageCaptureConfigBuilderWithEffect(effectMode, lensFacing);
         return imageCaptureConfigBuilder.getUseCaseConfig();
     }
 
     /**
-     * Creates a {@link PreviewConfig} object for specific {@link EffectMode} and
+     * Creates a {@link PreviewConfig} object for specific {@link ExtensionsManager.EffectMode} and
      * {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
@@ -278,7 +268,8 @@
      * @return A {@link PreviewConfig} object.
      */
     @NonNull
-    public static PreviewConfig createPreviewConfigWithEffect(@NonNull EffectMode effectMode,
+    public static PreviewConfig createPreviewConfigWithEffect(
+            @NonNull ExtensionsManager.EffectMode effectMode,
             @CameraSelector.LensFacing int lensFacing) {
         Preview.Builder previewBuilder =
                 createPreviewBuilderWithEffect(effectMode, lensFacing);
@@ -286,7 +277,7 @@
     }
 
     /**
-     * Creates an {@link ImageCapture} object for specific {@link EffectMode} and
+     * Creates an {@link ImageCapture} object for specific {@link ExtensionsManager.EffectMode} and
      * {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
@@ -294,13 +285,14 @@
      * @return An {@link ImageCapture} object.
      */
     @NonNull
-    public static ImageCapture createImageCaptureWithEffect(@NonNull EffectMode effectMode,
+    public static ImageCapture createImageCaptureWithEffect(
+            @NonNull ExtensionsManager.EffectMode effectMode,
             @CameraSelector.LensFacing int lensFacing) {
         return createImageCaptureConfigBuilderWithEffect(effectMode, lensFacing).build();
     }
 
     /**
-     * Creates a {@link Preview} object for specific {@link EffectMode} and
+     * Creates a {@link Preview} object for specific {@link ExtensionsManager.EffectMode} and
      * {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
@@ -308,14 +300,14 @@
      * @return A {@link Preview} object.
      */
     @NonNull
-    public static Preview createPreviewWithEffect(@NonNull EffectMode effectMode,
+    public static Preview createPreviewWithEffect(@NonNull ExtensionsManager.EffectMode effectMode,
             @CameraSelector.LensFacing int lensFacing) {
         return createPreviewBuilderWithEffect(effectMode, lensFacing).build();
     }
 
     /**
-     * Creates an {@link ImageCaptureExtenderImpl} object for specific {@link EffectMode} and
-     * {@link CameraSelector.LensFacing}.
+     * Creates an {@link ImageCaptureExtenderImpl} object for specific
+     * {@link ExtensionsManager.EffectMode} and {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
      * @param lensFacing The lens facing for the created object.
@@ -323,8 +315,8 @@
      */
     @NonNull
     public static ImageCaptureExtenderImpl createImageCaptureExtenderImpl(
-            @NonNull EffectMode effectMode, @CameraSelector.LensFacing int lensFacing)
-            throws CameraInfoUnavailableException, CameraAccessException {
+            @NonNull ExtensionsManager.EffectMode effectMode,
+            @CameraSelector.LensFacing int lensFacing) throws CameraAccessException {
         ImageCaptureExtenderImpl impl = null;
 
         switch (effectMode) {
@@ -359,17 +351,17 @@
     }
 
     /**
-     * Creates a {@link PreviewExtenderImpl} object for specific {@link EffectMode} and
-     * {@link CameraSelector.LensFacing}.
+     * Creates a {@link PreviewExtenderImpl} object for specific
+     * {@link ExtensionsManager.EffectMode} and {@link CameraSelector.LensFacing}.
      *
      * @param effectMode The effect mode for the created object.
      * @param lensFacing The lens facing for the created object.
      * @return A {@link PreviewExtenderImpl} object.
      */
     @NonNull
-    public static PreviewExtenderImpl createPreviewExtenderImpl(@NonNull EffectMode effectMode,
-            @CameraSelector.LensFacing int lensFacing)
-            throws CameraInfoUnavailableException, CameraAccessException {
+    public static PreviewExtenderImpl createPreviewExtenderImpl(
+            @NonNull ExtensionsManager.EffectMode effectMode,
+            @CameraSelector.LensFacing int lensFacing) throws CameraAccessException {
         PreviewExtenderImpl impl = null;
 
         switch (effectMode) {
@@ -404,33 +396,34 @@
     }
 
     /**
-     * Creates an {@link ImageCaptureExtender} object for specific {@link EffectMode} and
-     * {@link ImageCapture.Builder}.
+     * Creates an {@link androidx.camera.extensions.ImageCaptureExtender} object for specific
+     * {@link ExtensionsManager.EffectMode} and {@link ImageCapture.Builder}.
      *
      * @param effectMode The effect mode for the created object.
      * @param builder    The {@link ImageCapture.Builder} for the created object.
-     * @return An {@link ImageCaptureExtender} object.
+     * @return An {@link androidx.camera.extensions.ImageCaptureExtender} object.
      */
     @NonNull
-    public static ImageCaptureExtender createImageCaptureExtender(@NonNull EffectMode effectMode,
+    public static androidx.camera.extensions.ImageCaptureExtender createImageCaptureExtender(
+            @NonNull ExtensionsManager.EffectMode effectMode,
             @NonNull ImageCapture.Builder builder) {
-        ImageCaptureExtender extender = null;
+        androidx.camera.extensions.ImageCaptureExtender extender = null;
 
         switch (effectMode) {
             case HDR:
-                extender = HdrImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.HdrImageCaptureExtender.create(builder);
                 break;
             case BOKEH:
-                extender = BokehImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.BokehImageCaptureExtender.create(builder);
                 break;
             case BEAUTY:
-                extender = BeautyImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.BeautyImageCaptureExtender.create(builder);
                 break;
             case NIGHT:
-                extender = NightImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.NightImageCaptureExtender.create(builder);
                 break;
             case AUTO:
-                extender = AutoImageCaptureExtender.create(builder);
+                extender = androidx.camera.extensions.AutoImageCaptureExtender.create(builder);
                 break;
         }
         assertNotNull(extender);
@@ -439,33 +432,33 @@
     }
 
     /**
-     * Creates a {@link PreviewExtender} object for specific {@link EffectMode} and
-     * {@link Preview.Builder}.
+     * Creates a {@link androidx.camera.extensions.PreviewExtender} object for specific
+     * {@link ExtensionsManager.EffectMode} and {@link Preview.Builder}.
      *
      * @param effectMode The effect mode for the created object.
      * @param builder    The {@link Preview.Builder} for the created object.
-     * @return A {@link PreviewExtender} object.
+     * @return A {@link androidx.camera.extensions.PreviewExtender} object.
      */
     @NonNull
-    public static PreviewExtender createPreviewExtender(@NonNull EffectMode effectMode,
-            @NonNull Preview.Builder builder) {
-        PreviewExtender extender = null;
+    public static androidx.camera.extensions.PreviewExtender createPreviewExtender(
+            @NonNull ExtensionsManager.EffectMode effectMode, @NonNull Preview.Builder builder) {
+        androidx.camera.extensions.PreviewExtender extender = null;
 
         switch (effectMode) {
             case HDR:
-                extender = HdrPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.HdrPreviewExtender.create(builder);
                 break;
             case BOKEH:
-                extender = BokehPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.BokehPreviewExtender.create(builder);
                 break;
             case BEAUTY:
-                extender = BeautyPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.BeautyPreviewExtender.create(builder);
                 break;
             case NIGHT:
-                extender = NightPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.NightPreviewExtender.create(builder);
                 break;
             case AUTO:
-                extender = AutoPreviewExtender.create(builder);
+                extender = androidx.camera.extensions.AutoPreviewExtender.create(builder);
                 break;
         }
         assertNotNull(extender);
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
index f1468ac..97e663c 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
@@ -150,15 +150,14 @@
                                 public void onSuccess() {
                                     Logger.d(TAG, "Successfully initialized extensions");
                                     setInitialized(true);
-                                    completer.set(
-                                        ExtensionsAvailability.LIBRARY_AVAILABLE);
+                                    completer.set(ExtensionsAvailability.LIBRARY_AVAILABLE);
                                 }
 
                                 @Override
                                 public void onFailure(int error) {
                                     Logger.d(TAG, "Failed to initialize extensions");
-                                    completer.set(
-                                        ExtensionsAvailability.LIBRARY_UNAVAILABLE_ERROR_LOADING);
+                                    completer.set(ExtensionsAvailability
+                                            .LIBRARY_UNAVAILABLE_ERROR_LOADING);
                                 }
                                 },
                                 CameraXExecutors.mainThreadExecutor());
@@ -366,7 +365,14 @@
         }
     }
 
-    static void postExtensionsError(ExtensionsErrorListener.ExtensionsErrorCode errorCode) {
+    /**
+     * Posts extension error to the listener.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static void postExtensionsError(
+            @NonNull ExtensionsErrorListener.ExtensionsErrorCode errorCode) {
         synchronized (ERROR_LOCK) {
             final ExtensionsErrorListener listenerReference = sExtensionsErrorListener;
             if (listenerReference != null) {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
index f0fedcd..86bfdc4 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
@@ -148,11 +148,8 @@
 
     /**
      * Update extension related configs to the builder.
-     *
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static void updateBuilderConfig(@NonNull ImageCapture.Builder builder,
+    private static void updateBuilderConfig(@NonNull ImageCapture.Builder builder,
             @ExtensionMode.Mode int effectMode, @NonNull ImageCaptureExtenderImpl impl,
             @NonNull Context context) {
         CaptureProcessorImpl captureProcessor = impl.getCaptureProcessor();
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
index 0dc41cb..80da011 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
@@ -47,6 +47,7 @@
 import androidx.camera.extensions.internal.AdaptingCaptureStage;
 import androidx.camera.extensions.internal.AdaptingPreviewProcessor;
 import androidx.camera.extensions.internal.AdaptingRequestUpdateProcessor;
+import androidx.camera.extensions.internal.CloseableProcessor;
 import androidx.camera.extensions.internal.ExtensionVersion;
 import androidx.camera.extensions.internal.Version;
 import androidx.core.util.Consumer;
@@ -145,11 +146,8 @@
 
     /**
      * Update extension related configs to the builder.
-     *
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static void updateBuilderConfig(@NonNull Preview.Builder builder,
+    private static void updateBuilderConfig(@NonNull Preview.Builder builder,
             @ExtensionMode.Mode int effectMode, @NonNull PreviewExtenderImpl impl,
             @NonNull Context context) {
         PreviewExtenderAdapter previewExtenderAdapter;
@@ -394,15 +392,4 @@
             return null;
         }
     }
-
-    /**
-     * A processor that can be closed so that the underlying processing implementation is skipped,
-     * if it has been closed.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public interface CloseableProcessor {
-        void close();
-    }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingPreviewProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingPreviewProcessor.java
index 191870f..fad4afa 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingPreviewProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingPreviewProcessor.java
@@ -33,7 +33,6 @@
 import androidx.camera.core.impl.CameraCaptureResults;
 import androidx.camera.core.impl.CaptureProcessor;
 import androidx.camera.core.impl.ImageProxyBundle;
-import androidx.camera.extensions.PreviewExtender;
 import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
 import androidx.core.util.Preconditions;
 
@@ -45,8 +44,7 @@
 /**
  * A {@link CaptureProcessor} that calls a vendor provided preview processing implementation.
  */
-public final class AdaptingPreviewProcessor implements CaptureProcessor,
-        PreviewExtender.CloseableProcessor {
+public final class AdaptingPreviewProcessor implements CaptureProcessor, CloseableProcessor {
     private static final String TAG = "AdaptingPreviewProcesso";
     private final PreviewImageProcessorImpl mImpl;
     private BlockingCloseAccessCounter mAccessCounter = new BlockingCloseAccessCounter();
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessor.java
index 1b32169..c1445b9 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessor.java
@@ -27,7 +27,6 @@
 import androidx.camera.core.impl.CameraCaptureResults;
 import androidx.camera.core.impl.CaptureStage;
 import androidx.camera.core.impl.ImageInfoProcessor;
-import androidx.camera.extensions.PreviewExtender;
 import androidx.camera.extensions.impl.CaptureStageImpl;
 import androidx.camera.extensions.impl.PreviewExtenderImpl;
 import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
@@ -37,7 +36,7 @@
  * A {@link ImageInfoProcessor} that calls a vendor provided preview processing implementation.
  */
 public final class AdaptingRequestUpdateProcessor implements ImageInfoProcessor,
-        PreviewExtender.CloseableProcessor {
+        CloseableProcessor {
     private final PreviewExtenderImpl mPreviewExtenderImpl;
     private final RequestUpdateProcessorImpl mProcessorImpl;
     private BlockingCloseAccessCounter mAccessCounter = new BlockingCloseAccessCounter();
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/CloseableProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/CloseableProcessor.java
new file mode 100644
index 0000000..5fb4252
--- /dev/null
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/CloseableProcessor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 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.camera.extensions.internal;
+
+/**
+ * A processor that can be closed so that the underlying processing implementation is skipped,
+ * if it has been closed.
+ */
+public interface CloseableProcessor {
+
+    /**
+     * Close the processor.
+     */
+    void close();
+}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ImageCaptureConfigProvider.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ImageCaptureConfigProvider.java
index dbc33ff..48f770b 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ImageCaptureConfigProvider.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ImageCaptureConfigProvider.java
@@ -16,31 +16,61 @@
 
 package androidx.camera.extensions.internal;
 
+import static androidx.camera.extensions.internal.PreviewConfigProvider.OPTION_PREVIEW_CONFIG_PROVIDER_MODE;
+
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.os.Build;
+import android.util.Pair;
+import android.util.Size;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
+import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.camera.camera2.impl.CameraEventCallback;
+import androidx.camera.camera2.impl.CameraEventCallbacks;
 import androidx.camera.camera2.interop.Camera2CameraInfo;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraInfo;
 import androidx.camera.core.ImageCapture;
+import androidx.camera.core.Logger;
+import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CaptureBundle;
+import androidx.camera.core.impl.CaptureConfig;
+import androidx.camera.core.impl.CaptureStage;
+import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.ConfigProvider;
 import androidx.camera.core.impl.ImageCaptureConfig;
 import androidx.camera.core.impl.OptionsBundle;
 import androidx.camera.extensions.ExtensionMode;
-import androidx.camera.extensions.ImageCaptureExtender;
+import androidx.camera.extensions.ExtensionsErrorListener;
+import androidx.camera.extensions.ExtensionsManager;
 import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.BokehImageCaptureExtenderImpl;
+import androidx.camera.extensions.impl.CaptureProcessorImpl;
+import androidx.camera.extensions.impl.CaptureStageImpl;
 import androidx.camera.extensions.impl.HdrImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.NightImageCaptureExtenderImpl;
+import androidx.core.util.Consumer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Provides extensions related configs for image capture
  */
 public class ImageCaptureConfigProvider implements ConfigProvider<ImageCaptureConfig> {
+    private static final String TAG = "ImageCaptureConfigProvider";
+    static final Config.Option<Integer> OPTION_IMAGE_CAPTURE_CONFIG_PROVIDER_MODE =
+            Config.Option.create("camerax.extensions.imageCaptureConfigProvider.mode",
+                    Integer.class);
+
     private ImageCaptureExtenderImpl mImpl;
     private Context mContext;
     @ExtensionMode.Mode
@@ -90,8 +120,228 @@
 
         ImageCapture.Builder builder = new ImageCapture.Builder();
 
-        ImageCaptureExtender.updateBuilderConfig(builder, mEffectMode, mImpl, mContext);
+        updateBuilderConfig(builder, mEffectMode, mImpl, mContext);
 
         return builder.getUseCaseConfig();
     }
+
+    /**
+     * Update extension related configs to the builder.
+     */
+    private void updateBuilderConfig(@NonNull ImageCapture.Builder builder,
+            @ExtensionMode.Mode int effectMode, @NonNull ImageCaptureExtenderImpl impl,
+            @NonNull Context context) {
+        CaptureProcessorImpl captureProcessor = impl.getCaptureProcessor();
+        if (captureProcessor != null) {
+            builder.setCaptureProcessor(new AdaptingCaptureProcessor(captureProcessor));
+        }
+
+        if (impl.getMaxCaptureStage() > 0) {
+            builder.setMaxCaptureStages(impl.getMaxCaptureStage());
+        }
+
+        ImageCaptureEventAdapter imageCaptureEventAdapter = new ImageCaptureEventAdapter(impl,
+                context);
+        new Camera2ImplConfig.Extender<>(builder).setCameraEventCallback(
+                new CameraEventCallbacks(imageCaptureEventAdapter));
+        builder.setUseCaseEventCallback(imageCaptureEventAdapter);
+
+        try {
+            Consumer<Collection<UseCase>> attachedUseCasesUpdateListener =
+                    useCases -> checkPreviewEnabled(effectMode, useCases);
+            builder.setAttachedUseCasesUpdateListener(attachedUseCasesUpdateListener);
+        } catch (NoSuchMethodError e) {
+            // setAttachedUseCasesUpdateListener function may not exist in the used core library.
+            // Catches the NoSuchMethodError and make the extensions be able to be enabled but
+            // only the ExtensionsErrorListener does not work.
+            Logger.e(TAG, "Can't set attached use cases update listener.");
+        }
+
+        builder.setCaptureBundle(imageCaptureEventAdapter);
+        builder.getMutableConfig().insertOption(OPTION_IMAGE_CAPTURE_CONFIG_PROVIDER_MODE,
+                effectMode);
+
+        List<Pair<Integer, Size[]>> supportedResolutions = getSupportedResolutions(impl);
+        if (supportedResolutions != null) {
+            builder.setSupportedResolutions(supportedResolutions);
+        }
+    }
+
+    /**
+     * Get the supported resolutions.
+     */
+    @Nullable
+    private List<Pair<Integer, Size[]>> getSupportedResolutions(
+            @NonNull ImageCaptureExtenderImpl impl) {
+        if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_1) < 0) {
+            return null;
+        }
+
+        try {
+            return impl.getSupportedResolutions();
+        } catch (NoSuchMethodError e) {
+            Logger.e(TAG, "getSupportedResolution interface is not implemented in vendor library.");
+            return null;
+        }
+    }
+
+    private void checkPreviewEnabled(@ExtensionMode.Mode int effectMode,
+            Collection<UseCase> activeUseCases) {
+        boolean isPreviewExtenderEnabled = false;
+        boolean isMismatched = false;
+
+        // In case all use cases are unbound when doing the check.
+        if (activeUseCases == null || activeUseCases.isEmpty()) {
+            return;
+        }
+
+        for (UseCase useCase : activeUseCases) {
+            int previewExtenderMode = useCase.getCurrentConfig().retrieveOption(
+                    OPTION_PREVIEW_CONFIG_PROVIDER_MODE, ExtensionMode.NONE);
+
+            if (effectMode == previewExtenderMode) {
+                isPreviewExtenderEnabled = true;
+            } else if (previewExtenderMode != ExtensionMode.NONE) {
+                isMismatched = true;
+            }
+        }
+
+        if (isMismatched) {
+            ExtensionsManager.postExtensionsError(
+                    ExtensionsErrorListener.ExtensionsErrorCode.MISMATCHED_EXTENSIONS_ENABLED);
+        } else if (!isPreviewExtenderEnabled) {
+            ExtensionsManager.postExtensionsError(
+                    ExtensionsErrorListener.ExtensionsErrorCode.PREVIEW_EXTENSION_REQUIRED);
+        }
+    }
+
+    /**
+     * An implementation to adapt the OEM provided implementation to core.
+     */
+    private static class ImageCaptureEventAdapter extends CameraEventCallback implements
+            UseCase.EventCallback,
+            CaptureBundle {
+        @NonNull
+        private final ImageCaptureExtenderImpl mImpl;
+        @NonNull
+        private final Context mContext;
+        private final AtomicBoolean mActive = new AtomicBoolean(true);
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
+        private volatile int mEnabledSessionCount = 0;
+        @GuardedBy("mLock")
+        private volatile boolean mUnbind = false;
+
+        ImageCaptureEventAdapter(@NonNull ImageCaptureExtenderImpl impl,
+                @NonNull Context context) {
+            mImpl = impl;
+            mContext = context;
+        }
+
+        @OptIn(markerClass = ExperimentalCamera2Interop.class)
+        @Override
+        public void onAttach(@NonNull CameraInfo cameraInfo) {
+            if (mActive.get()) {
+                String cameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
+                CameraCharacteristics cameraCharacteristics =
+                        Camera2CameraInfo.extractCameraCharacteristics(cameraInfo);
+                mImpl.onInit(cameraId, cameraCharacteristics, mContext);
+            }
+        }
+
+        @Override
+        public void onDetach() {
+            synchronized (mLock) {
+                mUnbind = true;
+                if (mEnabledSessionCount == 0) {
+                    callDeInit();
+                }
+            }
+        }
+
+        private void callDeInit() {
+            if (mActive.get()) {
+                mImpl.onDeInit();
+                mActive.set(false);
+            }
+        }
+
+        @Override
+        @Nullable
+        public CaptureConfig onPresetSession() {
+            if (mActive.get()) {
+                CaptureStageImpl captureStageImpl = mImpl.onPresetSession();
+                if (captureStageImpl != null) {
+                    if (Build.VERSION.SDK_INT >= 28) {
+                        return new AdaptingCaptureStage(captureStageImpl).getCaptureConfig();
+                    } else {
+                        Logger.w(TAG, "The CaptureRequest parameters returned from "
+                                + "onPresetSession() will be passed to the camera device as part "
+                                + "of the capture session via "
+                                + "SessionConfiguration#setSessionParameters(CaptureRequest) "
+                                + "which only supported from API level 28!");
+                    }
+                }
+            }
+            return null;
+        }
+
+        @Override
+        @Nullable
+        public CaptureConfig onEnableSession() {
+            try {
+                if (mActive.get()) {
+                    CaptureStageImpl captureStageImpl = mImpl.onEnableSession();
+                    if (captureStageImpl != null) {
+                        return new AdaptingCaptureStage(captureStageImpl).getCaptureConfig();
+                    }
+                }
+
+                return null;
+            } finally {
+                synchronized (mLock) {
+                    mEnabledSessionCount++;
+                }
+            }
+        }
+
+        @Override
+        @Nullable
+        public CaptureConfig onDisableSession() {
+            try {
+                if (mActive.get()) {
+                    CaptureStageImpl captureStageImpl = mImpl.onDisableSession();
+                    if (captureStageImpl != null) {
+                        return new AdaptingCaptureStage(captureStageImpl).getCaptureConfig();
+                    }
+                }
+
+                return null;
+            } finally {
+                synchronized (mLock) {
+                    mEnabledSessionCount--;
+                    if (mEnabledSessionCount == 0 && mUnbind) {
+                        callDeInit();
+                    }
+                }
+            }
+        }
+
+        @Override
+        @Nullable
+        public List<CaptureStage> getCaptureStages() {
+            if (mActive.get()) {
+                List<CaptureStageImpl> captureStages = mImpl.getCaptureStages();
+                if (captureStages != null && !captureStages.isEmpty()) {
+                    ArrayList<CaptureStage> ret = new ArrayList<>();
+                    for (CaptureStageImpl s : captureStages) {
+                        ret.add(new AdaptingCaptureStage(s));
+                    }
+                    return ret;
+                }
+            }
+
+            return null;
+        }
+    }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/PreviewConfigProvider.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/PreviewConfigProvider.java
index e186004..63dcb65 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/PreviewConfigProvider.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/PreviewConfigProvider.java
@@ -16,31 +16,55 @@
 
 package androidx.camera.extensions.internal;
 
+import static androidx.camera.extensions.internal.ImageCaptureConfigProvider.OPTION_IMAGE_CAPTURE_CONFIG_PROVIDER_MODE;
+
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.os.Build;
+import android.util.Pair;
+import android.util.Size;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
+import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.camera.camera2.impl.CameraEventCallback;
+import androidx.camera.camera2.impl.CameraEventCallbacks;
 import androidx.camera.camera2.interop.Camera2CameraInfo;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraInfo;
+import androidx.camera.core.Logger;
 import androidx.camera.core.Preview;
+import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CaptureConfig;
+import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.ConfigProvider;
 import androidx.camera.core.impl.OptionsBundle;
 import androidx.camera.core.impl.PreviewConfig;
 import androidx.camera.extensions.ExtensionMode;
-import androidx.camera.extensions.PreviewExtender;
+import androidx.camera.extensions.ExtensionsErrorListener;
+import androidx.camera.extensions.ExtensionsManager;
 import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
 import androidx.camera.extensions.impl.BeautyPreviewExtenderImpl;
 import androidx.camera.extensions.impl.BokehPreviewExtenderImpl;
+import androidx.camera.extensions.impl.CaptureStageImpl;
 import androidx.camera.extensions.impl.HdrPreviewExtenderImpl;
 import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
 import androidx.camera.extensions.impl.PreviewExtenderImpl;
+import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
+import androidx.core.util.Consumer;
+
+import java.util.Collection;
+import java.util.List;
 
 /**
  * For providing extensions config for preview.
  */
 public class PreviewConfigProvider implements ConfigProvider<PreviewConfig> {
+    private static final String TAG = "PreviewConfigProvider";
+    static final Config.Option<Integer> OPTION_PREVIEW_CONFIG_PROVIDER_MODE = Config.Option.create(
+            "camerax.extensions.previewConfigProvider.mode", Integer.class);
     private PreviewExtenderImpl mImpl;
     private Context mContext;
     @ExtensionMode.Mode
@@ -90,8 +114,250 @@
         }
         Preview.Builder builder = new Preview.Builder();
 
-        PreviewExtender.updateBuilderConfig(builder, mEffectMode, mImpl, mContext);
+        updateBuilderConfig(builder, mEffectMode, mImpl, mContext);
 
         return builder.getUseCaseConfig();
     }
+
+    /**
+     * Update extension related configs to the builder.
+     */
+    private void updateBuilderConfig(@NonNull Preview.Builder builder,
+            @ExtensionMode.Mode int effectMode, @NonNull PreviewExtenderImpl impl,
+            @NonNull Context context) {
+        PreviewEventAdapter previewEventAdapter;
+
+        switch (impl.getProcessorType()) {
+            case PROCESSOR_TYPE_REQUEST_UPDATE_ONLY:
+                AdaptingRequestUpdateProcessor adaptingRequestUpdateProcessor =
+                        new AdaptingRequestUpdateProcessor(impl);
+                builder.setImageInfoProcessor(adaptingRequestUpdateProcessor);
+                previewEventAdapter = new PreviewEventAdapter(impl, context,
+                        adaptingRequestUpdateProcessor);
+                break;
+            case PROCESSOR_TYPE_IMAGE_PROCESSOR:
+                AdaptingPreviewProcessor adaptingPreviewProcessor = new
+                        AdaptingPreviewProcessor((PreviewImageProcessorImpl) impl.getProcessor());
+                builder.setCaptureProcessor(adaptingPreviewProcessor);
+                previewEventAdapter = new PreviewEventAdapter(impl, context,
+                        adaptingPreviewProcessor);
+                break;
+            default:
+                previewEventAdapter = new PreviewEventAdapter(impl, context, null);
+        }
+
+        new Camera2ImplConfig.Extender<>(builder).setCameraEventCallback(
+                new CameraEventCallbacks(previewEventAdapter));
+        builder.setUseCaseEventCallback(previewEventAdapter);
+
+        try {
+            Consumer<Collection<UseCase>> attachedUseCasesUpdateListener =
+                    useCases -> checkImageCaptureEnabled(effectMode, useCases);
+            builder.setAttachedUseCasesUpdateListener(attachedUseCasesUpdateListener);
+        } catch (NoSuchMethodError e) {
+            // setAttachedUseCasesUpdateListener function may not exist in the used core library.
+            // Catches the NoSuchMethodError and make the extensions be able to be enabled but
+            // only the ExtensionsErrorListener does not work.
+            Logger.e(TAG, "Can't set attached use cases update listener.");
+        }
+
+        builder.getMutableConfig().insertOption(OPTION_PREVIEW_CONFIG_PROVIDER_MODE, effectMode);
+        List<Pair<Integer, Size[]>> supportedResolutions = getSupportedResolutions(impl);
+        if (supportedResolutions != null) {
+            builder.setSupportedResolutions(supportedResolutions);
+        }
+    }
+
+    /**
+     * Get the resolutions.
+     */
+    @Nullable
+    private List<Pair<Integer, Size[]>> getSupportedResolutions(@NonNull PreviewExtenderImpl impl) {
+        if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_1) < 0) {
+            return null;
+        }
+
+        try {
+            return impl.getSupportedResolutions();
+        } catch (NoSuchMethodError e) {
+            Logger.e(TAG, "getSupportedResolution interface is not implemented in vendor library.");
+            return null;
+        }
+    }
+
+    private void checkImageCaptureEnabled(@ExtensionMode.Mode int effectMode,
+            Collection<UseCase> activeUseCases) {
+        boolean isImageCaptureExtenderEnabled = false;
+        boolean isMismatched = false;
+
+        // In case all use cases are unbound when doing the check.
+        if (activeUseCases == null || activeUseCases.isEmpty()) {
+            return;
+        }
+
+        for (UseCase useCase : activeUseCases) {
+            int imageCaptureExtenderMode = useCase.getCurrentConfig().retrieveOption(
+                    OPTION_IMAGE_CAPTURE_CONFIG_PROVIDER_MODE,
+                    ExtensionMode.NONE);
+
+            if (effectMode == imageCaptureExtenderMode) {
+                isImageCaptureExtenderEnabled = true;
+            } else if (imageCaptureExtenderMode != ExtensionMode.NONE) {
+                isMismatched = true;
+            }
+        }
+
+        if (isMismatched) {
+            ExtensionsManager.postExtensionsError(
+                    ExtensionsErrorListener.ExtensionsErrorCode.MISMATCHED_EXTENSIONS_ENABLED);
+        } else if (!isImageCaptureExtenderEnabled) {
+            ExtensionsManager.postExtensionsError(
+                    ExtensionsErrorListener.ExtensionsErrorCode.IMAGE_CAPTURE_EXTENSION_REQUIRED);
+        }
+    }
+
+    /**
+     * An implementation to adapt the OEM provided implementation to core.
+     */
+    private static class PreviewEventAdapter extends CameraEventCallback implements
+            UseCase.EventCallback {
+        @NonNull
+        final PreviewExtenderImpl mImpl;
+        @NonNull
+        private final Context mContext;
+
+        final CloseableProcessor mCloseableProcessor;
+
+        // Once the adapter has set mActive to false a new instance needs to be created
+        @GuardedBy("mLock")
+        volatile boolean mActive = true;
+        final Object mLock = new Object();
+        @GuardedBy("mLock")
+        private volatile int mEnabledSessionCount = 0;
+        @GuardedBy("mLock")
+        private volatile boolean mUnbind = false;
+
+        PreviewEventAdapter(@NonNull PreviewExtenderImpl impl,
+                @NonNull Context context, @Nullable CloseableProcessor closeableProcessor) {
+            mImpl = impl;
+            mContext = context;
+            mCloseableProcessor = closeableProcessor;
+        }
+
+        @OptIn(markerClass = ExperimentalCamera2Interop.class)
+        @Override
+        public void onAttach(@NonNull CameraInfo cameraInfo) {
+            synchronized (mLock) {
+                if (mActive) {
+                    String cameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
+                    CameraCharacteristics cameraCharacteristics =
+                            Camera2CameraInfo.extractCameraCharacteristics(cameraInfo);
+                    mImpl.onInit(cameraId, cameraCharacteristics, mContext);
+                }
+            }
+        }
+
+        @Override
+        public void onDetach() {
+            synchronized (mLock) {
+                mUnbind = true;
+                if (mEnabledSessionCount == 0) {
+                    callDeInit();
+                }
+            }
+        }
+
+        private void callDeInit() {
+            synchronized (mLock) {
+                if (mActive) {
+                    if (mCloseableProcessor != null) {
+                        mCloseableProcessor.close();
+                    }
+                    mImpl.onDeInit();
+                    mActive = false;
+                }
+            }
+        }
+
+        @Override
+        @Nullable
+        public CaptureConfig onPresetSession() {
+            synchronized (mLock) {
+                CaptureStageImpl captureStageImpl = mImpl.onPresetSession();
+                if (captureStageImpl != null) {
+                    if (Build.VERSION.SDK_INT >= 28) {
+                        return new AdaptingCaptureStage(captureStageImpl).getCaptureConfig();
+                    } else {
+                        Logger.w(TAG, "The CaptureRequest parameters returned from "
+                                + "onPresetSession() will be passed to the camera device as part "
+                                + "of the capture session via "
+                                + "SessionConfiguration#setSessionParameters(CaptureRequest) "
+                                + "which only supported from API level 28!");
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        @Nullable
+        public CaptureConfig onEnableSession() {
+            try {
+                synchronized (mLock) {
+                    if (mActive) {
+                        CaptureStageImpl captureStageImpl = mImpl.onEnableSession();
+                        if (captureStageImpl != null) {
+                            return new AdaptingCaptureStage(captureStageImpl).getCaptureConfig();
+                        }
+                    }
+                }
+
+                return null;
+            } finally {
+                synchronized (mLock) {
+                    mEnabledSessionCount++;
+                }
+            }
+        }
+
+        @Override
+        @Nullable
+        public CaptureConfig onDisableSession() {
+            try {
+                synchronized (mLock) {
+                    if (mActive) {
+                        CaptureStageImpl captureStageImpl = mImpl.onDisableSession();
+                        if (captureStageImpl != null) {
+                            return new AdaptingCaptureStage(captureStageImpl).getCaptureConfig();
+                        }
+                    }
+                }
+
+                return null;
+            } finally {
+                synchronized (mLock) {
+                    mEnabledSessionCount--;
+                    if (mEnabledSessionCount == 0 && mUnbind) {
+                        callDeInit();
+                    }
+                }
+            }
+        }
+
+        @Override
+        @Nullable
+        public CaptureConfig onRepeating() {
+            synchronized (mLock) {
+                if (mActive) {
+                    CaptureStageImpl captureStageImpl = mImpl.getCaptureStage();
+                    if (captureStageImpl != null) {
+                        return new AdaptingCaptureStage(captureStageImpl).getCaptureConfig();
+                    }
+                }
+            }
+
+            return null;
+        }
+    }
 }
diff --git a/camera/camera-lifecycle/build.gradle b/camera/camera-lifecycle/build.gradle
index c6c7cea..42d4b73 100644
--- a/camera/camera-lifecycle/build.gradle
+++ b/camera/camera-lifecycle/build.gradle
@@ -18,8 +18,6 @@
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -28,21 +26,21 @@
 
 dependencies {
     api("androidx.lifecycle:lifecycle-common:2.1.0")
-    api(GUAVA_LISTENABLE_FUTURE)
+    api(libs.guavaListenableFuture)
     api(project(":camera:camera-core"))
     implementation("androidx.core:core:1.1.0")
-    implementation(AUTO_VALUE_ANNOTATIONS)
+    implementation(libs.autoValueAnnotations)
 
-    annotationProcessor(AUTO_VALUE)
+    annotationProcessor(libs.autoValue)
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
     androidTestImplementation(project(":camera:camera-testing"))
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
     androidTestImplementation("androidx.annotation:annotation-experimental:1.1.0")
     androidTestImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     androidTestImplementation(project(":internal-testutils-truth"))
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 0f3fd42..8613c38 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -15,11 +15,9 @@
  */
 
 
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -28,27 +26,27 @@
 }
 
 dependencies {
-    implementation(ANDROIDX_TEST_CORE)
-    implementation(ANDROIDX_TEST_RULES)
-    implementation(ANDROIDX_TEST_UIAUTOMATOR)
+    implementation(libs.testCore)
+    implementation(libs.testRules)
+    implementation(libs.testUiautomator)
     api("androidx.annotation:annotation:1.2.0")
-    implementation(ESPRESSO_CORE)
-    implementation(GUAVA_LISTENABLE_FUTURE)
+    implementation(libs.espressoCore)
+    implementation(libs.guavaListenableFuture)
     implementation("androidx.appcompat:appcompat:1.1.0")
     api(project(":camera:camera-core"))
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.test.espresso:espresso-idling-resource:3.1.0")
-    implementation(JUNIT)
-    implementation(KOTLIN_STDLIB)
-    implementation(KOTLIN_COROUTINES_ANDROID)
+    implementation(libs.junit)
+    implementation(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesAndroid)
 
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.mockitoCore)
 }
 
 android {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
index 4645b33..28da413 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
@@ -27,14 +27,16 @@
  * throws the AssumptionViolatedException to ignore the test if the test environment is not in the
  * lab. Useful for the tests not needed to run on the PostSubmit.
  *
- * To use this [TestRule] do the following. <br></br><br></br>
+ * To use this [TestRule], do the following. <br></br><br></br>
  *
  * Add the Rule to your JUnit test. <br></br><br></br>
  * `LabTestRule mLabTestRule = new LabTestRule();
 ` *
  * <br></br><br></br>
  *
- * Add the [LabTestOnly] annotation to your test case. <br></br><br></br>
+ * Add only one of [LabTestOnly], [LabTestFrontCamera] or, [LabTestRearCamera] annotation to your
+ * test case.
+ * <br></br><br></br>
  * `public void yourTestCase() {
  *
  * }
@@ -50,6 +52,22 @@
     @Retention(AnnotationRetention.RUNTIME)
     annotation class LabTestOnly()
 
+    /**
+     * The annotation for tests that only want to run on the CameraX lab environment with
+     * enabling front camera.
+     */
+    @Target(AnnotationTarget.FUNCTION)
+    @Retention(AnnotationRetention.RUNTIME)
+    annotation class LabTestFrontCamera()
+
+    /**
+     * The annotation for tests that only want to run on the CameraX lab environment with
+     * enabling rear camera.
+     */
+    @Target(AnnotationTarget.FUNCTION)
+    @Retention(AnnotationRetention.RUNTIME)
+    annotation class LabTestRearCamera()
+
     class LabTestStatement(private val statement: Statement) :
         Statement() {
 
@@ -63,9 +81,38 @@
         }
     }
 
+    class LabTestFrontCameraStatement(private val statement: Statement) :
+        Statement() {
+
+        @Throws(Throwable::class)
+        override fun evaluate() {
+            // Only test in CameraX lab environment and the loggable tag will be set when running
+            // the CameraX e2e test with enabling front camera.
+            assumeTrue(Log.isLoggable("frontCameraE2E", Log.DEBUG))
+            statement.evaluate()
+        }
+    }
+
+    class LabTestRearCameraStatement(private val statement: Statement) :
+        Statement() {
+
+        @Throws(Throwable::class)
+        override fun evaluate() {
+            // Only test in CameraX lab environment and the loggable tag will be set when running
+            // the CameraX e2e test with enabling rear camera.
+            assumeTrue(Log.isLoggable("rearCameraE2E", Log.DEBUG))
+            statement.evaluate()
+        }
+    }
+
     override fun apply(base: Statement, description: Description): Statement {
+
         return if (description.getAnnotation(LabTestOnly::class.java) != null) {
             LabTestStatement(base)
+        } else if (description.getAnnotation(LabTestFrontCamera::class.java) != null) {
+            LabTestFrontCameraStatement(base)
+        } else if (description.getAnnotation(LabTestRearCamera::class.java) != null) {
+            LabTestRearCameraStatement(base)
         } else {
             base
         }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index e42f23c..24f1a46 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -22,7 +22,6 @@
 import android.graphics.Rect;
 
 import androidx.annotation.NonNull;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.FocusMeteringResult;
 import androidx.camera.core.ImageCapture;
@@ -136,7 +135,6 @@
 
     @NonNull
     @Override
-    @ExperimentalExposureCompensation
     public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
         return Futures.immediateFuture(null);
     }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
index ac45541..cec24f5 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
@@ -24,7 +24,6 @@
 import androidx.annotation.Nullable;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraState;
-import androidx.camera.core.ExperimentalExposureCompensation;
 import androidx.camera.core.ExposureState;
 import androidx.camera.core.TorchState;
 import androidx.camera.core.ZoomState;
@@ -138,7 +137,6 @@
 
     @NonNull
     @Override
-    @ExperimentalExposureCompensation
     public ExposureState getExposureState() {
         return new ExposureState() {
             @Override
diff --git a/camera/camera-video/build.gradle b/camera/camera-video/build.gradle
index 70b825f..a3e03e9 100644
--- a/camera/camera-video/build.gradle
+++ b/camera/camera-video/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryVersions
+
 import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -30,38 +30,38 @@
     api(project(":camera:camera-core"))
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
-    implementation(AUTO_VALUE_ANNOTATIONS)
+    implementation(libs.autoValueAnnotations)
 
-    annotationProcessor(AUTO_VALUE)
+    annotationProcessor(libs.autoValue)
 
     // TODO(leohuang): We need this for assertThrows. Point back to the AndroidX shared version if
     //  it is ever upgraded.
     testImplementation("junit:junit:4.13")
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.mockitoCore)
     testImplementation(project(":camera:camera-testing"), {
         exclude group: "androidx.camera", module: "camera-core"
     })
 
     androidTestImplementation(project(path: ":camera:camera-camera2"))
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(project(":camera:camera-testing"))
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
     androidTestImplementation(project(":concurrent:concurrent-futures-ktx"))
     androidTestImplementation(project(":internal-testutils-truth"))
-    androidTestImplementation MOCKITO_KOTLIN, {
+    androidTestImplementation libs.mockitoKotlin, {
         exclude group: 'org.mockito' // to keep control on the mockito version
     }
 }
diff --git a/camera/camera-view/api/current.txt b/camera/camera-view/api/current.txt
index 160857f..a56d9d4 100644
--- a/camera/camera-view/api/current.txt
+++ b/camera/camera-view/api/current.txt
@@ -4,13 +4,21 @@
   public abstract class CameraController {
     method @MainThread public void clearImageAnalysisAnalyzer();
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
     method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
     method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+    method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
     method @MainThread public int getImageAnalysisBackpressureStrategy();
     method @MainThread public int getImageAnalysisImageQueueDepth();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
     method @MainThread public int getImageCaptureFlashMode();
+    method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+    method @MainThread public int getImageCaptureMode();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
     method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getVideoCaptureTargetSize();
     method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
     method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
     method @MainThread public boolean isImageAnalysisEnabled();
@@ -20,12 +28,19 @@
     method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
     method @MainThread public void setEnabledUseCases(int);
     method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
     method @MainThread public void setImageAnalysisBackpressureStrategy(int);
     method @MainThread public void setImageAnalysisImageQueueDepth(int);
+    method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public void setImageCaptureFlashMode(int);
+    method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+    method @MainThread public void setImageCaptureMode(int);
+    method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
     method @MainThread public void setPinchToZoomEnabled(boolean);
+    method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public void setTapToFocusEnabled(boolean);
+    method @MainThread public void setVideoCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
     method @MainThread public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
     method @MainThread public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
@@ -33,6 +48,14 @@
     field public static final int IMAGE_CAPTURE = 1; // 0x1
   }
 
+  public static class CameraController.OutputSize {
+    ctor public CameraController.OutputSize(int);
+    ctor public CameraController.OutputSize(android.util.Size);
+    method public int getAspectRatio();
+    method public android.util.Size? getResolution();
+    field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+  }
+
   public final class LifecycleCameraController extends androidx.camera.view.CameraController {
     ctor public LifecycleCameraController(android.content.Context);
     method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
@@ -75,5 +98,13 @@
     enum_constant public static final androidx.camera.view.PreviewView.StreamState STREAMING;
   }
 
+  public abstract class RotationReceiver {
+    ctor public RotationReceiver(android.content.Context);
+    method public boolean canDetectOrientation();
+    method public void disable();
+    method public void enable();
+    method public abstract void onRotationChanged(int);
+  }
+
 }
 
diff --git a/camera/camera-view/api/public_plus_experimental_current.txt b/camera/camera-view/api/public_plus_experimental_current.txt
index 2faf2d6..798e132 100644
--- a/camera/camera-view/api/public_plus_experimental_current.txt
+++ b/camera/camera-view/api/public_plus_experimental_current.txt
@@ -4,13 +4,21 @@
   public abstract class CameraController {
     method @MainThread public void clearImageAnalysisAnalyzer();
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
     method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
     method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+    method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
     method @MainThread @androidx.camera.core.ImageAnalysis.BackpressureStrategy public int getImageAnalysisBackpressureStrategy();
     method @MainThread public int getImageAnalysisImageQueueDepth();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
     method @MainThread @androidx.camera.core.ImageCapture.FlashMode public int getImageCaptureFlashMode();
+    method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+    method @MainThread public int getImageCaptureMode();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
     method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getVideoCaptureTargetSize();
     method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
     method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
     method @MainThread public boolean isImageAnalysisEnabled();
@@ -22,12 +30,19 @@
     method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
     method @MainThread public void setEnabledUseCases(int);
     method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
     method @MainThread public void setImageAnalysisBackpressureStrategy(@androidx.camera.core.ImageAnalysis.BackpressureStrategy int);
     method @MainThread public void setImageAnalysisImageQueueDepth(int);
+    method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public void setImageCaptureFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+    method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+    method @MainThread public void setImageCaptureMode(@androidx.camera.core.ImageCapture.CaptureMode int);
+    method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
     method @MainThread public void setPinchToZoomEnabled(boolean);
+    method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public void setTapToFocusEnabled(boolean);
+    method @MainThread public void setVideoCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
     method @MainThread @androidx.camera.view.video.ExperimentalVideo public void startRecording(androidx.camera.view.video.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.view.video.OnVideoSavedCallback);
     method @MainThread @androidx.camera.view.video.ExperimentalVideo public void stopRecording();
@@ -38,6 +53,14 @@
     field @androidx.camera.view.video.ExperimentalVideo public static final int VIDEO_CAPTURE = 4; // 0x4
   }
 
+  public static class CameraController.OutputSize {
+    ctor public CameraController.OutputSize(@androidx.camera.core.AspectRatio.Ratio int);
+    ctor public CameraController.OutputSize(android.util.Size);
+    method public int getAspectRatio();
+    method public android.util.Size? getResolution();
+    field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+  }
+
   public final class LifecycleCameraController extends androidx.camera.view.CameraController {
     ctor public LifecycleCameraController(android.content.Context);
     method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
@@ -83,6 +106,14 @@
     enum_constant public static final androidx.camera.view.PreviewView.StreamState STREAMING;
   }
 
+  public abstract class RotationReceiver {
+    ctor public RotationReceiver(android.content.Context);
+    method public boolean canDetectOrientation();
+    method public void disable();
+    method public void enable();
+    method public abstract void onRotationChanged(int);
+  }
+
 }
 
 package androidx.camera.view.transform {
diff --git a/camera/camera-view/api/restricted_current.txt b/camera/camera-view/api/restricted_current.txt
index 56cdeaa..c880826 100644
--- a/camera/camera-view/api/restricted_current.txt
+++ b/camera/camera-view/api/restricted_current.txt
@@ -4,13 +4,21 @@
   public abstract class CameraController {
     method @MainThread public void clearImageAnalysisAnalyzer();
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
     method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
     method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+    method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
     method @MainThread @androidx.camera.core.ImageAnalysis.BackpressureStrategy public int getImageAnalysisBackpressureStrategy();
     method @MainThread public int getImageAnalysisImageQueueDepth();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
     method @MainThread @androidx.camera.core.ImageCapture.FlashMode public int getImageCaptureFlashMode();
+    method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+    method @MainThread public int getImageCaptureMode();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
     method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getVideoCaptureTargetSize();
     method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
     method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
     method @MainThread public boolean isImageAnalysisEnabled();
@@ -20,12 +28,19 @@
     method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
     method @MainThread public void setEnabledUseCases(int);
     method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
     method @MainThread public void setImageAnalysisBackpressureStrategy(@androidx.camera.core.ImageAnalysis.BackpressureStrategy int);
     method @MainThread public void setImageAnalysisImageQueueDepth(int);
+    method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public void setImageCaptureFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+    method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+    method @MainThread public void setImageCaptureMode(@androidx.camera.core.ImageCapture.CaptureMode int);
+    method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
     method @MainThread public void setPinchToZoomEnabled(boolean);
+    method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public void setTapToFocusEnabled(boolean);
+    method @MainThread public void setVideoCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
     method @MainThread public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
     method @MainThread public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
@@ -33,6 +48,14 @@
     field public static final int IMAGE_CAPTURE = 1; // 0x1
   }
 
+  public static class CameraController.OutputSize {
+    ctor public CameraController.OutputSize(@androidx.camera.core.AspectRatio.Ratio int);
+    ctor public CameraController.OutputSize(android.util.Size);
+    method public int getAspectRatio();
+    method public android.util.Size? getResolution();
+    field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+  }
+
   public final class LifecycleCameraController extends androidx.camera.view.CameraController {
     ctor public LifecycleCameraController(android.content.Context);
     method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
@@ -75,5 +98,13 @@
     enum_constant public static final androidx.camera.view.PreviewView.StreamState STREAMING;
   }
 
+  public abstract class RotationReceiver {
+    ctor public RotationReceiver(android.content.Context);
+    method public boolean canDetectOrientation();
+    method public void disable();
+    method public void enable();
+    method public abstract void onRotationChanged(int);
+  }
+
 }
 
diff --git a/camera/camera-view/build.gradle b/camera/camera-view/build.gradle
index e32f7a9..43e55a9 100644
--- a/camera/camera-view/build.gradle
+++ b/camera/camera-view/build.gradle
@@ -19,8 +19,6 @@
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -35,23 +33,23 @@
     api("androidx.camera:camera-core:${VIEW_ATOMIC_GROUP_PINNED_VER}")
     implementation("androidx.camera:camera-lifecycle:${VIEW_ATOMIC_GROUP_PINNED_VER}")
     implementation("androidx.annotation:annotation-experimental:1.1.0-rc01")
-    implementation(GUAVA_LISTENABLE_FUTURE)
+    implementation(libs.guavaListenableFuture)
     implementation("androidx.core:core:1.3.2")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
-    implementation(AUTO_VALUE_ANNOTATIONS)
+    implementation(libs.autoValueAnnotations)
     implementation("androidx.appcompat:appcompat:1.1.0")
     // Added for annotation-experimental
-    compileOnly(KOTLIN_STDLIB)
+    compileOnly(libs.kotlinStdlib)
 
-    annotationProcessor(AUTO_VALUE)
+    annotationProcessor(libs.autoValue)
 
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(MOCKITO_CORE)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(TRUTH)
-    testImplementation(ANDROIDX_TEST_RULES)
-    testImplementation(ANDROIDX_TEST_CORE)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.mockitoCore)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.truth)
+    testImplementation(libs.testRules)
+    testImplementation(libs.testCore)
     testImplementation(project(":camera:camera-testing")) {
         // Ensure camera-testing does not pull in camera-core project dependency which will
         // override pinned dependency.
@@ -63,18 +61,18 @@
         // override pinned dependency.
         exclude(group:"androidx.camera", module:"camera-core")
     }
-    androidTestImplementation(MOCKITO_CORE)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.mockitoCore)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.truth)
     androidTestImplementation("androidx.camera:camera-camera2:${VIEW_ATOMIC_GROUP_PINNED_VER}")
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
 }
 android {
     defaultConfig {
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
index 2cab702..2fe7a79 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
@@ -16,12 +16,15 @@
 
 package androidx.camera.view;
 
+import static androidx.camera.view.CameraController.OutputSize.UNASSIGNED_ASPECT_RATIO;
+
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Size;
 import android.view.Display;
 
 import androidx.annotation.DoNotInline;
@@ -34,6 +37,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
+import androidx.camera.core.AspectRatio;
 import androidx.camera.core.Camera;
 import androidx.camera.core.CameraControl;
 import androidx.camera.core.CameraInfo;
@@ -56,6 +60,7 @@
 import androidx.camera.core.VideoCapture;
 import androidx.camera.core.ViewPort;
 import androidx.camera.core.ZoomState;
+import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.utils.Threads;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.Futures;
@@ -150,32 +155,50 @@
     // Synthetic access
     @SuppressWarnings("WeakerAccess")
     @NonNull
-    final Preview mPreview;
+    Preview mPreview;
+
+    @Nullable
+    OutputSize mPreviewTargetSize;
 
     // Synthetic access
     @SuppressWarnings("WeakerAccess")
     @NonNull
-    final ImageCapture mImageCapture;
+    ImageCapture mImageCapture;
+
+    @Nullable
+    OutputSize mImageCaptureTargetSize;
+
+    @Nullable
+    Executor mImageCaptureIoExecutor;
 
     @Nullable
     private Executor mAnalysisExecutor;
 
     @Nullable
+    private Executor mAnalysisBackgroundExecutor;
+
+    @Nullable
     private ImageAnalysis.Analyzer mAnalysisAnalyzer;
 
     @NonNull
     ImageAnalysis mImageAnalysis;
 
+    @Nullable
+    OutputSize mImageAnalysisTargetSize;
+
     // Synthetic access
     @SuppressWarnings("WeakerAccess")
     @NonNull
-    final VideoCapture mVideoCapture;
+    VideoCapture mVideoCapture;
 
     // Synthetic access
     @SuppressWarnings("WeakerAccess")
     @NonNull
     final AtomicBoolean mVideoIsRecording = new AtomicBoolean(false);
 
+    @Nullable
+    OutputSize mVideoCaptureOutputSize;
+
     // The latest bound camera.
     // Synthetic access
     @SuppressWarnings("WeakerAccess")
@@ -205,7 +228,7 @@
     // Synthetic access
     @SuppressWarnings("WeakerAccess")
     @NonNull
-    final SensorRotationListener mSensorRotationListener;
+    final RotationReceiver mRotationReceiver;
 
     @Nullable
     private final DisplayRotationListener mDisplayRotationListener;
@@ -242,7 +265,7 @@
 
         // Listen to motion sensor reading and set target rotation for ImageCapture and
         // VideoCapture.
-        mSensorRotationListener = new SensorRotationListener(mAppContext) {
+        mRotationReceiver = new RotationReceiver(mAppContext) {
             @Override
             public void onRotationChanged(int rotation) {
                 mImageAnalysis.setTargetRotation(rotation);
@@ -272,7 +295,7 @@
      * Gets a {@link ListenableFuture} that completes when camera initialization completes.
      *
      * <p> This future may fail with an {@link InitializationException} and associated cause that
-     * can be retrieved by {@link Throwable#getCause()). The cause will be a
+     * can be retrieved by {@link Throwable#getCause()}. The cause will be a
      * {@link CameraUnavailableException} if it fails to access any camera during initialization.
      *
      * <p> In the rare case that the future fails with {@link CameraUnavailableException}, the
@@ -375,6 +398,35 @@
         return (mEnabledUseCases & useCaseMask) != 0;
     }
 
+    /**
+     * Sets the target aspect ratio or target resolution based on {@link OutputSize}.
+     */
+    private void setTargetOutputSize(@NonNull ImageOutputConfig.Builder<?> builder,
+            @Nullable OutputSize outputSize) {
+        if (outputSize == null) {
+            return;
+        }
+        if (outputSize.getResolution() != null) {
+            builder.setTargetResolution(outputSize.getResolution());
+        } else if (outputSize.getAspectRatio() != UNASSIGNED_ASPECT_RATIO) {
+            builder.setTargetAspectRatio(outputSize.getAspectRatio());
+        } else {
+            Logger.e(TAG, "Invalid target surface size. " + outputSize);
+        }
+    }
+
+    /**
+     * Checks if two {@link OutputSize} are equal.
+     */
+    private boolean isOutputSizeEqual(
+            @Nullable OutputSize currentSize,
+            @Nullable OutputSize newSize) {
+        if (currentSize == newSize) {
+            return true;
+        }
+        return currentSize != null && currentSize.equals(newSize);
+    }
+
     // ------------------
     // Preview use case.
     // ------------------
@@ -419,20 +471,69 @@
     private void startListeningToRotationEvents() {
         getDisplayManager().registerDisplayListener(mDisplayRotationListener,
                 new Handler(Looper.getMainLooper()));
-        if (mSensorRotationListener.canDetectOrientation()) {
-            mSensorRotationListener.enable();
+        if (mRotationReceiver.canDetectOrientation()) {
+            mRotationReceiver.enable();
         }
     }
 
     private void stopListeningToRotationEvents() {
         getDisplayManager().unregisterDisplayListener(mDisplayRotationListener);
-        mSensorRotationListener.disable();
+        mRotationReceiver.disable();
     }
 
     private DisplayManager getDisplayManager() {
         return (DisplayManager) mAppContext.getSystemService(Context.DISPLAY_SERVICE);
     }
 
+    /**
+     * Sets the intended output size for {@link Preview}.
+     *
+     * <p> The value is used as a hint when determining the resolution and aspect ratio of the
+     * preview. The actual output may differ from the requested value due to device constraints.
+     *
+     * <p> When set to null, the output will be based on the default config of {@link Preview}.
+     *
+     * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+     * To avoid this, set the target size before controller is bound to lifecycle.
+     *
+     * @param targetSize the intended output size for {@link Preview}.
+     * @see Preview.Builder#setTargetAspectRatio(int)
+     * @see Preview.Builder#setTargetResolution(Size)
+     */
+    @MainThread
+    public void setPreviewTargetSize(@Nullable OutputSize targetSize) {
+        Threads.checkMainThread();
+        if (isOutputSizeEqual(mPreviewTargetSize, targetSize)) {
+            return;
+        }
+        mPreviewTargetSize = targetSize;
+        unbindPreviewAndRecreate();
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Returns the intended output size for {@link Preview} set by
+     * {@link #setPreviewTargetSize(OutputSize)}, or null if not set.
+     */
+    @MainThread
+    @Nullable
+    public OutputSize getPreviewTargetSize() {
+        Threads.checkMainThread();
+        return mPreviewTargetSize;
+    }
+
+    /**
+     * Unbinds {@link Preview} and recreates with the latest parameters.
+     */
+    private void unbindPreviewAndRecreate() {
+        if (isCameraInitialized()) {
+            mCameraProvider.unbind(mPreview);
+        }
+        Preview.Builder builder = new Preview.Builder();
+        setTargetOutputSize(builder, mPreviewTargetSize);
+        mPreview = builder.build();
+    }
+
     // ----------------------
     // ImageCapture UseCase.
     // ----------------------
@@ -546,6 +647,120 @@
         mImageCapture.takePicture(executor, callback);
     }
 
+    /**
+     * Sets the image capture mode.
+     *
+     * <p>Valid capture modes are {@link ImageCapture.CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY},
+     * which prioritizes latency over image quality, or
+     * {@link ImageCapture.CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY},
+     * which prioritizes image quality over latency.
+     *
+     * @param captureMode the requested image capture mode.
+     */
+    @MainThread
+    public void setImageCaptureMode(@ImageCapture.CaptureMode int captureMode) {
+        Threads.checkMainThread();
+        if (mImageCapture.getCaptureMode() == captureMode) {
+            return;
+        }
+        unbindImageCaptureAndRecreate(captureMode);
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Returns the image capture mode.
+     *
+     * @see ImageCapture#getCaptureMode()
+     */
+    @MainThread
+    public int getImageCaptureMode() {
+        Threads.checkMainThread();
+        return mImageCapture.getCaptureMode();
+    }
+
+    /**
+     * Sets the intended image size for {@link ImageCapture}.
+     *
+     * <p> The value is used as a hint when determining the resolution and aspect ratio of
+     * the captured image. The actual output may differ from the requested value due to device
+     * constraints.
+     *
+     * <p> When set to null, the output will be based on the default config of {@link ImageCapture}.
+     *
+     * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+     * To avoid this, set the target size before controller is bound to lifecycle.
+     *
+     * @param targetSize the intended image size for {@link ImageCapture}.
+     */
+    @MainThread
+    public void setImageCaptureTargetSize(@Nullable OutputSize targetSize) {
+        Threads.checkMainThread();
+        if (isOutputSizeEqual(mImageCaptureTargetSize, targetSize)) {
+            return;
+        }
+        mImageCaptureTargetSize = targetSize;
+        unbindImageCaptureAndRecreate(getImageCaptureMode());
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Returns the intended output size for {@link ImageCapture} set by
+     * {@link #setImageCaptureTargetSize(OutputSize)}, or null if not set.
+     */
+    @MainThread
+    @Nullable
+    public OutputSize getImageCaptureTargetSize() {
+        Threads.checkMainThread();
+        return mImageCaptureTargetSize;
+    }
+
+    /**
+     * Sets the default executor that will be used for {@link ImageCapture} IO tasks.
+     *
+     * <p> This executor will be used for any IO tasks specifically for {@link ImageCapture},
+     * such as {@link #takePicture(ImageCapture.OutputFileOptions, Executor,
+     * ImageCapture.OnImageSavedCallback)}. If no executor is set, then a default Executor
+     * specifically for IO will be used instead.
+     *
+     * @param executor The executor which will be used for IO tasks.
+     *                 TODO(b/187842789) add @see link for ImageCapture.
+     */
+    @MainThread
+    public void setImageCaptureIoExecutor(@Nullable Executor executor) {
+        Threads.checkMainThread();
+        if (mImageCaptureIoExecutor == executor) {
+            return;
+        }
+        mImageCaptureIoExecutor = executor;
+        unbindImageCaptureAndRecreate(mImageCapture.getCaptureMode());
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Gets the default executor for {@link ImageCapture} IO tasks.
+     */
+    @MainThread
+    @Nullable
+    public Executor getImageCaptureIoExecutor() {
+        Threads.checkMainThread();
+        return mImageCaptureIoExecutor;
+    }
+
+    /**
+     * Unbinds {@link ImageCapture} and recreates with the latest parameters.
+     */
+    private void unbindImageCaptureAndRecreate(int imageCaptureMode) {
+        if (isCameraInitialized()) {
+            mCameraProvider.unbind(mImageCapture);
+        }
+        ImageCapture.Builder builder = new ImageCapture.Builder().setCaptureMode(imageCaptureMode);
+        setTargetOutputSize(builder, mImageCaptureTargetSize);
+        if (mImageCaptureIoExecutor != null) {
+            builder.setIoExecutor(mImageCaptureIoExecutor);
+        }
+        mImageCapture = builder.build();
+    }
+
     // -----------------
     // Image analysis
     // -----------------
@@ -673,19 +888,94 @@
     }
 
     /**
-     * Unbinds {@link ImageAnalysis} and recreates with the given parameters.
+     * Sets the intended output size for {@link ImageAnalysis}.
      *
-     * <p> This is necessary because unlike other use cases, {@link ImageAnalysis}'s parameters
-     * cannot be updated without recreating the use case.
+     * <p> The value is used as a hint when determining the resolution and aspect ratio of
+     * the output buffer. The actual output may differ from the requested value due to device
+     * constraints.
+     *
+     * <p> When set to null, the output will be based on the default config of
+     * {@link ImageAnalysis}.
+     *
+     * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+     * To avoid this, set the target size before controller is bound to lifecycle.
+     *
+     * @param targetSize the intended output size for {@link ImageAnalysis}.
+     * @see ImageAnalysis.Builder#setTargetAspectRatio(int)
+     * @see ImageAnalysis.Builder#setTargetResolution(Size)
+     */
+    @MainThread
+    public void setImageAnalysisTargetSize(@Nullable OutputSize targetSize) {
+        Threads.checkMainThread();
+        if (isOutputSizeEqual(mImageAnalysisTargetSize, targetSize)) {
+            return;
+        }
+        mImageAnalysisTargetSize = targetSize;
+        unbindImageAnalysisAndRecreate(
+                mImageAnalysis.getBackpressureStrategy(),
+                mImageAnalysis.getImageQueueDepth());
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Returns the intended output size for {@link ImageAnalysis} set by
+     * {@link #setImageAnalysisTargetSize(OutputSize)}, or null if not set.
+     */
+    @MainThread
+    @Nullable
+    public OutputSize getImageAnalysisTargetSize() {
+        Threads.checkMainThread();
+        return mImageAnalysisTargetSize;
+    }
+
+    /**
+     * Sets the executor that will be used for {@link ImageAnalysis} background tasks.
+     *
+     * <p>If not set, the background executor will default to an automatically generated
+     * {@link Executor}.
+     *
+     * @param executor the executor for {@link ImageAnalysis} background tasks.
+     * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor)
+     */
+    @MainThread
+    public void setImageAnalysisBackgroundExecutor(@Nullable Executor executor) {
+        Threads.checkMainThread();
+        if (mAnalysisBackgroundExecutor == executor) {
+            return;
+        }
+        mAnalysisBackgroundExecutor = executor;
+        unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(),
+                mImageAnalysis.getImageQueueDepth());
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Gets the default executor for {@link ImageAnalysis} background tasks.
+     *
+     * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor)
+     */
+    @MainThread
+    @Nullable
+    public Executor getImageAnalysisBackgroundExecutor() {
+        Threads.checkMainThread();
+        return mAnalysisBackgroundExecutor;
+    }
+
+    /**
+     * Unbinds {@link ImageAnalysis} and recreates with the latest parameters.
      */
     private void unbindImageAnalysisAndRecreate(int strategy, int imageQueueDepth) {
         if (isCameraInitialized()) {
             mCameraProvider.unbind(mImageAnalysis);
         }
-        mImageAnalysis = new ImageAnalysis.Builder()
+        ImageAnalysis.Builder builder = new ImageAnalysis.Builder()
                 .setBackpressureStrategy(strategy)
-                .setImageQueueDepth(imageQueueDepth)
-                .build();
+                .setImageQueueDepth(imageQueueDepth);
+        setTargetOutputSize(builder, mImageAnalysisTargetSize);
+        if (mAnalysisBackgroundExecutor != null) {
+            builder.setBackgroundExecutor(mAnalysisBackgroundExecutor);
+        }
+        mImageAnalysis = builder.build();
         if (mAnalysisExecutor != null && mAnalysisAnalyzer != null) {
             mImageAnalysis.setAnalyzer(mAnalysisExecutor, mAnalysisAnalyzer);
         }
@@ -765,6 +1055,53 @@
         return mVideoIsRecording.get();
     }
 
+    /**
+     * Sets the intended video size for {@code VideoCapture}.
+     *
+     * <p> The value is used as a hint when determining the resolution and aspect ratio of
+     * the video. The actual output may differ from the requested value due to device constraints.
+     *
+     * <p> When set to null, the output will be based on the default config of {@code VideoCapture}.
+     *
+     * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+     * To avoid this, set the target size before controller is bound to lifecycle.
+     *
+     * @param targetSize the intended video size for {@code VideoCapture}.
+     */
+    @MainThread
+    public void setVideoCaptureTargetSize(@Nullable OutputSize targetSize) {
+        Threads.checkMainThread();
+        if (isOutputSizeEqual(mVideoCaptureOutputSize, targetSize)) {
+            return;
+        }
+        mVideoCaptureOutputSize = targetSize;
+        unbindVideoAndRecreate();
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Returns the intended output size for {@code VideoCapture} set by
+     * {@link #setVideoCaptureTargetSize(OutputSize)}, or null if not set.
+     */
+    @MainThread
+    @Nullable
+    public OutputSize getVideoCaptureTargetSize() {
+        Threads.checkMainThread();
+        return mVideoCaptureOutputSize;
+    }
+
+    /**
+     * Unbinds VideoCapture and recreate with the latest parameters.
+     */
+    private void unbindVideoAndRecreate() {
+        if (isCameraInitialized()) {
+            mCameraProvider.unbind(mVideoCapture);
+        }
+        VideoCapture.Builder builder = new VideoCapture.Builder();
+        setTargetOutputSize(builder, mVideoCaptureOutputSize);
+        mVideoCapture = builder.build();
+    }
+
     // -----------------
     // Camera control
     // -----------------
@@ -985,6 +1322,12 @@
     /**
      * Gets the {@link CameraInfo} of the currently attached camera.
      *
+     * <p> For info available directly through CameraController as well as {@link CameraInfo},
+     * it's recommended to use the ones with CameraController, e.g. {@link #getTorchState()} v.s.
+     * {@link CameraInfo#getTorchState()}. {@link CameraInfo} is a lower-layer API and may
+     * require more steps to achieve the same effect, and will not maintain values when switching
+     * between cameras.
+     *
      * @return the {@link CameraInfo} of the current camera. Returns null if camera is not ready.
      * @see Camera#getCameraInfo()
      */
@@ -996,6 +1339,25 @@
     }
 
     /**
+     * Gets the {@link CameraControl} of the currently attached camera.
+     *
+     * <p> For controls available directly through CameraController as well as
+     * {@link CameraControl}, it's recommended to use the ones with CameraController, e.g.
+     * {@link #setLinearZoom(float)} v.s. {@link CameraControl#setLinearZoom(float)}.
+     * CameraControl is a lower-layer API and may require more steps to achieve the same effect,
+     * and will not maintain control values when switching between cameras.
+     *
+     * @return the {@link CameraControl} of the current camera. Returns null if camera is not ready.
+     * @see Camera#getCameraControl()
+     */
+    @Nullable
+    @MainThread
+    public CameraControl getCameraControl() {
+        Threads.checkMainThread();
+        return mCamera == null ? null : mCamera.getCameraControl();
+    }
+
+    /**
      * Sets current zoom by ratio.
      *
      * <p>Valid zoom values range from {@link ZoomState#getMinZoomRatio()} to
@@ -1221,4 +1583,84 @@
             return context.getAttributionTag();
         }
     }
+
+    /**
+     * Represents the output size of a {@link UseCase}.
+     *
+     * <p> This class is a preferred output size to be used with {@link CameraController}. The
+     * preferred output size can be based on either resolution or aspect ratio, but not both.
+     *
+     * @see #setImageAnalysisTargetSize(OutputSize)
+     * @see #setPreviewTargetSize(OutputSize)
+     * @see #setImageCaptureTargetSize(OutputSize)
+     * @see #setVideoCaptureTargetSize(OutputSize)
+     */
+    public static class OutputSize {
+
+        /**
+         * A value that represents the aspect ratio is not assigned.
+         */
+        public static final int UNASSIGNED_ASPECT_RATIO = -1;
+
+        /**
+         * Possible value for {@link #getAspectRatio()}
+         *
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(value = {UNASSIGNED_ASPECT_RATIO, AspectRatio.RATIO_4_3, AspectRatio.RATIO_16_9})
+        public @interface OutputAspectRatio {
+        }
+
+        @OutputAspectRatio
+        private final int mAspectRatio;
+
+        @Nullable
+        private final Size mResolution;
+
+        /**
+         * Creates a {@link OutputSize} that is based on aspect ratio.
+         *
+         * @see Preview.Builder#setTargetAspectRatio(int)
+         * @see ImageAnalysis.Builder#setTargetAspectRatio(int)
+         */
+        public OutputSize(@AspectRatio.Ratio int aspectRatio) {
+            Preconditions.checkArgument(aspectRatio != UNASSIGNED_ASPECT_RATIO);
+            mAspectRatio = aspectRatio;
+            mResolution = null;
+        }
+
+        /**
+         * Creates a {@link OutputSize} that is based on resolution.
+         *
+         * @see Preview.Builder#setTargetResolution(Size)
+         * @see ImageAnalysis.Builder#setTargetResolution(Size)
+         */
+        public OutputSize(@NonNull Size resolution) {
+            Preconditions.checkNotNull(resolution);
+            mAspectRatio = UNASSIGNED_ASPECT_RATIO;
+            mResolution = resolution;
+        }
+
+        /**
+         * Gets the value of aspect ratio.
+         *
+         * @return {@link #UNASSIGNED_ASPECT_RATIO} if the size is not based on aspect ratio.
+         */
+        @OutputAspectRatio
+        public int getAspectRatio() {
+            return mAspectRatio;
+        }
+
+        /**
+         * Gets the value of resolution.
+         *
+         * @return null if the size is not based on resolution.
+         */
+        @Nullable
+        public Size getResolution() {
+            return mResolution;
+        }
+    }
 }
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/RotationReceiver.java b/camera/camera-view/src/main/java/androidx/camera/view/RotationReceiver.java
new file mode 100644
index 0000000..b64fad4
--- /dev/null
+++ b/camera/camera-view/src/main/java/androidx/camera/view/RotationReceiver.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2021 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.camera.view;
+
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.view.OrientationEventListener;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.UseCase;
+
+/**
+ * Helper class for receiving rotation updates from the {@link SensorManager} when the
+ * orientation of the device has changed.
+ *
+ * <p> This class is an wrapper of {@link OrientationEventListener} that notifies the app about
+ * physical orientation changes in the format of {@link Surface} rotation. It's useful when the
+ * device UI is in a fixed portrait or landscape orientation, while the app still wants to set the
+ * {@link UseCase} target rotation based on the device's physical orientation.
+ *
+ * <pre><code>
+ * rotationReceiver = new RotationReceiver(context) {
+ *         public void onRotationChanged(int rotation) {
+ *             mImageCapture.setTargetRotation(rotation);
+ *         }
+ * };
+ * if (rotationReceiver.canDetectOrientation()) {
+ *     rotationReceiver.enable();
+ * }
+ *
+ * // Disable it when it's no longer needed.
+ * rotationReceiver.disable();
+ * </code></pre>
+ *
+ * @see OrientationEventListener
+ */
+public abstract class RotationReceiver {
+
+    private static final int INVALID_SURFACE_ROTATION = -1;
+
+    // Synthetic access
+    @SuppressWarnings("WeakerAccess")
+    int mRotation = INVALID_SURFACE_ROTATION;
+
+    private final OrientationEventListener mOrientationEventListener;
+
+    public RotationReceiver(@NonNull Context context) {
+        mOrientationEventListener = new OrientationEventListener(context) {
+            @Override
+            public void onOrientationChanged(int orientation) {
+                if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
+                    // Short-circuit if orientation is unknown. Unknown rotation can't be handled
+                    // so it shouldn't be sent.
+                    return;
+                }
+
+                int newRotation;
+                if (orientation >= 315 || orientation < 45) {
+                    newRotation = Surface.ROTATION_0;
+                } else if (orientation >= 225) {
+                    newRotation = Surface.ROTATION_90;
+                } else if (orientation >= 135) {
+                    newRotation = Surface.ROTATION_180;
+                } else {
+                    newRotation = Surface.ROTATION_270;
+                }
+                if (mRotation != newRotation) {
+                    mRotation = newRotation;
+                    onRotationChanged(newRotation);
+                }
+            }
+        };
+    }
+
+    /**
+     * Checks if the RotationReceiver can detect orientation changes.
+     *
+     * @see OrientationEventListener#canDetectOrientation()
+     */
+    public boolean canDetectOrientation() {
+        return mOrientationEventListener.canDetectOrientation();
+    }
+
+    /**
+     * Enables the RotationReceiver so it will monitor the sensor and call onRotationChanged when
+     * the device orientation changes.
+     *
+     * <p> By default, the receiver is not enabled.
+     *
+     * @see OrientationEventListener#enable()
+     */
+    public void enable() {
+        mOrientationEventListener.enable();
+    }
+
+    /**
+     * Disables the RotationReceiver.
+     *
+     * @see OrientationEventListener#disable()
+     */
+    public void disable() {
+        mOrientationEventListener.disable();
+    }
+
+    /**
+     * Called when the physical rotation of the device changes.
+     *
+     * <p> The rotation is one of the {@link Surface} rotations mapped from orientation
+     * degrees.
+     *
+     * <table summary="Orientation degrees to Surface rotation mapping">
+     * <tr><th>Orientation degrees</th><th>Surface rotation</th></tr>
+     * <tr><td>[-45°, 45°)</td><td>{@link Surface#ROTATION_0}</td></tr>
+     * <tr><td>[45°, 135°)</td><td>{@link Surface#ROTATION_270}</td></tr>
+     * <tr><td>[135°, 225°)</td><td>{@link Surface#ROTATION_180}</td></tr>
+     * <tr><td>[225°, 315°)</td><td>{@link Surface#ROTATION_90}</td></tr>
+     * </table>
+     *
+     * @see OrientationEventListener#onOrientationChanged(int)
+     */
+    public abstract void onRotationChanged(int rotation);
+}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/SensorRotationListener.java b/camera/camera-view/src/main/java/androidx/camera/view/SensorRotationListener.java
deleted file mode 100644
index d0919d4..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/SensorRotationListener.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2020 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.camera.view;
-
-import android.content.Context;
-import android.view.OrientationEventListener;
-import android.view.Surface;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Listens to motion sensor reading and converts the orientation degrees to {@link Surface}
- * rotation.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class SensorRotationListener extends OrientationEventListener {
-
-    public static final int INVALID_SURFACE_ROTATION = -1;
-
-    private int mRotation = INVALID_SURFACE_ROTATION;
-
-    public SensorRotationListener(@NonNull Context context) {
-        super(context);
-    }
-
-    @Override
-    public void onOrientationChanged(int orientation) {
-        if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
-            // Short-circuit if orientation is unknown. Unknown rotation can't be handled so it
-            // shouldn't be sent.
-            return;
-        }
-
-        int newRotation;
-        if (orientation >= 315 || orientation < 45) {
-            newRotation = Surface.ROTATION_0;
-        } else if (orientation >= 225) {
-            newRotation = Surface.ROTATION_90;
-        } else if (orientation >= 135) {
-            newRotation = Surface.ROTATION_180;
-        } else {
-            newRotation = Surface.ROTATION_270;
-        }
-        if (mRotation != newRotation) {
-            mRotation = newRotation;
-            onRotationChanged(newRotation);
-        }
-    }
-
-    /**
-     * Invoked when rotation changes.
-     *
-     * <p> The output rotation is defined as the UI Surface rotation, or what the Surface rotation
-     * should be if the app's orientation is not locked.
-     */
-    public abstract void onRotationChanged(int rotation);
-}
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
index 916d316..ce2e610 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
+++ b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
@@ -18,10 +18,17 @@
 
 import android.content.Context
 import android.os.Build
+import android.util.Size
 import android.view.Surface
+import androidx.camera.core.AspectRatio
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraX
 import androidx.camera.core.CameraXConfig
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.impl.ImageAnalysisConfig
+import androidx.camera.core.impl.ImageCaptureConfig
+import androidx.camera.core.impl.ImageOutputConfig
 import androidx.camera.testing.fakes.FakeAppConfig
 import androidx.test.annotation.UiThreadTest
 import androidx.test.core.app.ApplicationProvider
@@ -33,6 +40,7 @@
 import org.robolectric.RobolectricTestRunner
 import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
+import java.util.concurrent.Executors
 
 /**
  * Unit tests for [CameraController].
@@ -43,6 +51,11 @@
 public class CameraControllerTest {
 
     private val context = ApplicationProvider.getApplicationContext<Context>()
+    private lateinit var controller: CameraController
+    private val targetSizeWithAspectRatio =
+        CameraController.OutputSize(AspectRatio.RATIO_16_9)
+    private val targetSizeWithResolution =
+        CameraController.OutputSize(Size(1080, 1960))
 
     @Before
     public fun setUp() {
@@ -50,6 +63,7 @@
             FakeAppConfig.create()
         ).build()
         CameraX.initialize(context, cameraXConfig).get()
+        controller = LifecycleCameraController(context)
     }
 
     @After
@@ -59,24 +73,139 @@
 
     @UiThreadTest
     @Test
-    public fun sensorRotationChanges_useCaseTargetRotationUpdated() {
-        // Arrange.
-        val controller = LifecycleCameraController(context)
+    public fun setPreviewAspectRatio() {
+        controller.previewTargetSize = targetSizeWithAspectRatio
+        assertThat(controller.previewTargetSize).isEqualTo(targetSizeWithAspectRatio)
 
+        val config = controller.mPreview.currentConfig as ImageOutputConfig
+        assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setPreviewResolution() {
+        controller.previewTargetSize = targetSizeWithResolution
+        assertThat(controller.previewTargetSize).isEqualTo(targetSizeWithResolution)
+
+        val config = controller.mPreview.currentConfig as ImageOutputConfig
+        assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setAnalysisAspectRatio() {
+        controller.imageAnalysisTargetSize = targetSizeWithAspectRatio
+        assertThat(controller.imageAnalysisTargetSize).isEqualTo(targetSizeWithAspectRatio)
+
+        val config = controller.mImageAnalysis.currentConfig as ImageOutputConfig
+        assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setAnalysisBackgroundExecutor() {
+        val executor = Executors.newSingleThreadExecutor()
+        controller.imageAnalysisBackgroundExecutor = executor
+        assertThat(controller.imageAnalysisBackgroundExecutor).isEqualTo(executor)
+        val config = controller.mImageAnalysis.currentConfig as ImageAnalysisConfig
+        assertThat(config.backgroundExecutor).isEqualTo(executor)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setAnalysisQueueDepth() {
+        controller.imageAnalysisImageQueueDepth = 100
+        assertThat(controller.imageAnalysisImageQueueDepth).isEqualTo(100)
+        assertThat(controller.mImageAnalysis.imageQueueDepth).isEqualTo(100)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setAnalysisBackpressureStrategy() {
+        controller.imageAnalysisBackpressureStrategy = ImageAnalysis.STRATEGY_BLOCK_PRODUCER
+        assertThat(controller.imageAnalysisBackpressureStrategy)
+            .isEqualTo(ImageAnalysis.STRATEGY_BLOCK_PRODUCER)
+        assertThat(controller.mImageAnalysis.backpressureStrategy)
+            .isEqualTo(ImageAnalysis.STRATEGY_BLOCK_PRODUCER)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setImageCaptureResolution() {
+        controller.imageCaptureTargetSize = targetSizeWithResolution
+        assertThat(controller.imageCaptureTargetSize).isEqualTo(targetSizeWithResolution)
+
+        val config = controller.mImageCapture.currentConfig as ImageOutputConfig
+        assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setImageCaptureAspectRatio() {
+        controller.imageCaptureTargetSize = targetSizeWithAspectRatio
+        assertThat(controller.imageCaptureTargetSize).isEqualTo(targetSizeWithAspectRatio)
+
+        val config = controller.mImageCapture.currentConfig as ImageOutputConfig
+        assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setImageCaptureMode() {
+        controller.imageCaptureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
+        assertThat(controller.imageCaptureMode)
+            .isEqualTo(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+        assertThat(controller.mImageCapture.captureMode)
+            .isEqualTo(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setImageCaptureIoExecutor() {
+        val ioExecutor = Executors.newSingleThreadExecutor()
+        controller.imageCaptureIoExecutor = ioExecutor
+        assertThat(controller.imageCaptureIoExecutor).isEqualTo(ioExecutor)
+        val config = controller.mImageCapture.currentConfig as ImageCaptureConfig
+        assertThat(config.ioExecutor).isEqualTo(ioExecutor)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setVideoCaptureResolution() {
+        controller.videoCaptureTargetSize = targetSizeWithResolution
+        assertThat(controller.videoCaptureTargetSize).isEqualTo(targetSizeWithResolution)
+
+        val config = controller.mVideoCapture.currentConfig as ImageOutputConfig
+        assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun setVideoCaptureAspectRatio() {
+        controller.videoCaptureTargetSize = targetSizeWithAspectRatio
+        assertThat(controller.videoCaptureTargetSize).isEqualTo(targetSizeWithAspectRatio)
+
+        val config = controller.mVideoCapture.currentConfig as ImageOutputConfig
+        assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+    }
+
+    @UiThreadTest
+    @Test
+    public fun sensorRotationChanges_useCaseTargetRotationUpdated() {
         // Act.
-        controller.mSensorRotationListener.onRotationChanged(Surface.ROTATION_180)
+        controller.mRotationReceiver.onRotationChanged(Surface.ROTATION_180)
 
         // Assert.
         assertThat(controller.mImageAnalysis.targetRotation).isEqualTo(Surface.ROTATION_180)
         assertThat(controller.mImageCapture.targetRotation).isEqualTo(Surface.ROTATION_180)
-        // TODO(b/177276479): verify VideoCapture once it supports getTargetRotation().
+        val videoConfig = controller.mVideoCapture.currentConfig as ImageOutputConfig
+        assertThat(videoConfig.targetRotation).isEqualTo(Surface.ROTATION_180)
     }
 
     @UiThreadTest
     @Test
     public fun setSelectorBeforeBound_selectorSet() {
         // Arrange.
-        val controller = LifecycleCameraController(context)
         assertThat(controller.cameraSelector.lensFacing).isEqualTo(CameraSelector.LENS_FACING_BACK)
 
         // Act.
diff --git a/camera/integration-tests/camerapipetestapp/build.gradle b/camera/integration-tests/camerapipetestapp/build.gradle
index 4aa9ef6..1caa678 100644
--- a/camera/integration-tests/camerapipetestapp/build.gradle
+++ b/camera/integration-tests/camerapipetestapp/build.gradle
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.CONSTRAINT_LAYOUT
 
 plugins {
     id("AndroidXPlugin")
@@ -51,7 +51,7 @@
 
 dependencies {
     // Internal library
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
 
     // Android Support Library
     implementation("androidx.appcompat:appcompat:1.1.0")
@@ -61,15 +61,15 @@
     implementation(project(":camera:camera-camera2-pipe"))
 
     // Testing framework
-    testImplementation(TRUTH)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(TRUTH)
-    debugImplementation(ANDROIDX_TEST_CORE)
+    testImplementation(libs.truth)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.truth)
+    debugImplementation(libs.testCore)
 }
diff --git a/camera/integration-tests/coretestapp/build.gradle b/camera/integration-tests/coretestapp/build.gradle
index 1fd48a3..61455aa 100644
--- a/camera/integration-tests/coretestapp/build.gradle
+++ b/camera/integration-tests/coretestapp/build.gradle
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
+
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
-import static androidx.build.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.CONSTRAINT_LAYOUT
 
 plugins {
     id("AndroidXPlugin")
@@ -75,35 +76,37 @@
 
     // Android Support Library
     api(CONSTRAINT_LAYOUT, { transitive = true })
-    implementation(GUAVA_ANDROID)
-    implementation(ESPRESSO_IDLING_RESOURCE)
+    implementation(libs.guavaAndroid)
+    implementation(libs.espressoIdlingResource)
+    // MLKit library: Barcode scanner
+    implementation(libs.mlkitBarcode, excludes.mlkit)
 
     // 3P library
-    debugImplementation(LEAKCANARY)
+    debugImplementation(libs.leakcanary)
     // Testing resource dependency for manifest
     debugImplementation(project(":camera:camera-testing"))
 
     // Testing framework
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(ESPRESSO_IDLING_RESOURCE)
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
-    androidTestImplementation(LEAKCANARY)
-    androidTestImplementation(LEAKCANARY_INSTRUMENTATION)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.espressoIdlingResource)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
+    androidTestImplementation(libs.leakcanary)
+    androidTestImplementation(libs.leakcanaryInstrumentation)
+    androidTestImplementation(libs.truth)
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(project(":concurrent:concurrent-futures"))
     androidTestImplementation(project(":concurrent:concurrent-futures-ktx"))
     androidTestImplementation(project(":internal-testutils-runtime"))
     androidTestImplementation(project(":lifecycle:lifecycle-runtime-testing"))
 
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
 }
 
 // Allow usage of Kotlin's @OptIn.
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
new file mode 100644
index 0000000..7a667a2
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2021 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.camera.integration.core
+
+import android.content.Context
+import android.util.Log
+import android.util.Size
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraX
+import androidx.camera.core.CameraXConfig
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.LabTestRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import com.google.mlkit.vision.barcode.Barcode.FORMAT_QR_CODE
+import com.google.mlkit.vision.barcode.BarcodeScanner
+import com.google.mlkit.vision.barcode.BarcodeScannerOptions
+import com.google.mlkit.vision.barcode.BarcodeScanning
+import com.google.mlkit.vision.common.InputImage
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+// The integration-tests for MLKit vision barcode component with CameraX ImageAnalysis use case.
+@LargeTest
+@RunWith(Parameterized::class)
+class MLKitBarcodeTest(
+    private val resolution: Size,
+    private val cameraConfig: CameraXConfig
+) {
+
+    @get:Rule
+    val cameraRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+    @get:Rule
+    val labTest: LabTestRule = LabTestRule()
+
+    companion object {
+        private const val DETECT_TIMEOUT = 5_000L
+        private const val TAG = "MLKitVisionTest"
+
+        // TODO(b/189279877) Add different CameraXConfig (Camera2Config, CameraPipeConfig)
+        //  as parameters.
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun data() = listOf(
+            arrayOf(Size(640, 480), Camera2Config.defaultConfig()),
+            arrayOf(Size(1280, 720), Camera2Config.defaultConfig())
+        )
+    }
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+    private lateinit var camera: CameraUseCaseAdapter
+    // For MK Kit Barcode scanner
+    private lateinit var barcodeScanner: BarcodeScanner
+
+    @Before
+    fun setup() {
+        CameraX.initialize(context, cameraConfig).get(10, TimeUnit.SECONDS)
+
+        barcodeScanner = BarcodeScanning.getClient(
+            BarcodeScannerOptions.Builder().setBarcodeFormats(FORMAT_QR_CODE).build()
+        )
+    }
+
+    @After
+    fun tearDown(): Unit = runBlocking {
+        if (::camera.isInitialized) {
+            // TODO: The removeUseCases() call might be removed after clarifying the
+            // abortCaptures() issue in b/162314023
+            withContext(Dispatchers.Main) {
+                camera.removeUseCases(camera.useCases)
+            }
+        }
+        CameraX.shutdown().get(10, TimeUnit.SECONDS)
+
+        if (::barcodeScanner.isInitialized) {
+            barcodeScanner.close()
+        }
+    }
+
+    @LabTestRule.LabTestFrontCamera
+    @Test
+    fun barcodeDetectViaFontCamera() {
+        val imageAnalysis = initImageAnalysis()
+
+        camera = CameraUtil.createCameraAndAttachUseCase(
+            context,
+            CameraSelector.DEFAULT_FRONT_CAMERA,
+            imageAnalysis
+        )
+        assertBarcodeDetect(imageAnalysis)
+    }
+
+    @LabTestRule.LabTestRearCamera
+    @Test
+    fun barcodeDetectViaRearCamera() {
+        val imageAnalysis = initImageAnalysis()
+
+        camera = CameraUtil.createCameraAndAttachUseCase(
+            context,
+            CameraSelector.DEFAULT_BACK_CAMERA,
+            imageAnalysis
+        )
+        assertBarcodeDetect(imageAnalysis)
+    }
+
+    private fun assertBarcodeDetect(imageAnalysis: ImageAnalysis) {
+        val latchForBarcodeDetect = CountDownLatch(4)
+
+        imageAnalysis.setAnalyzer(
+            Dispatchers.Main.asExecutor(),
+            { imageProxy ->
+                barcodeScanner.process(
+                    InputImage.fromMediaImage(
+                        imageProxy.image!!,
+                        imageProxy.imageInfo.rotationDegrees
+                    )
+                )
+                    .addOnSuccessListener { barcodes ->
+                        barcodes.forEach {
+                            if ("Hi, CamX!" == it.displayValue) {
+                                latchForBarcodeDetect.countDown()
+                                Log.d(TAG, "barcode display value: {${it.displayValue}} ")
+                            }
+                        }
+                    }
+                    .addOnFailureListener { exception ->
+                        Log.e(TAG, "processImage onFailure: $exception")
+                    }
+                    // When the image is from CameraX analysis use case, must call image.close() on
+                    // received images when finished using them. Otherwise, new images may not be
+                    // received or the camera may stall.
+                    .addOnCompleteListener {
+                        imageProxy.close()
+                    }
+            }
+        )
+
+        // Verify it is the CameraX lab test environment and can detect qr-code.
+        assertTrue(latchForBarcodeDetect.await(DETECT_TIMEOUT, TimeUnit.MILLISECONDS))
+    }
+
+    private fun initImageAnalysis(): ImageAnalysis {
+        return ImageAnalysis.Builder()
+            .setTargetName("ImageAnalysis")
+            .setTargetResolution(resolution)
+            .build()
+    }
+}
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index c990c34..bf2dd58 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -195,7 +195,6 @@
     private FutureCallback<Integer> mEVFutureCallback = new FutureCallback<Integer>() {
 
         @Override
-        @OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
         public void onSuccess(@Nullable Integer result) {
             CameraInfo cameraInfo = getCameraInfo();
             if (cameraInfo != null) {
@@ -324,7 +323,6 @@
         return mPhotoToggle.isChecked() && cameraInfo != null && cameraInfo.hasFlashUnit();
     }
 
-    @OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
     private boolean isExposureCompensationSupported() {
         CameraInfo cameraInfo = getCameraInfo();
         return cameraInfo != null
@@ -469,7 +467,6 @@
         });
     }
 
-    @OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
     private void setUpEVButton() {
         mPlusEV.setOnClickListener(v -> {
             Objects.requireNonNull(getCameraInfo());
diff --git a/camera/integration-tests/extensionstestapp/build.gradle b/camera/integration-tests/extensionstestapp/build.gradle
index bdcea02..77a4380 100644
--- a/camera/integration-tests/extensionstestapp/build.gradle
+++ b/camera/integration-tests/extensionstestapp/build.gradle
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.CONSTRAINT_LAYOUT
 
 plugins {
     id("AndroidXPlugin")
@@ -38,7 +38,7 @@
     implementation(project(":camera:camera-lifecycle"))
     implementation(project(":camera:camera-view"))
     implementation("androidx.test.espresso:espresso-idling-resource:3.1.0")
-    implementation(GUAVA_LISTENABLE_FUTURE)
+    implementation(libs.guavaListenableFuture)
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
 
     // Android Support Library
@@ -46,14 +46,14 @@
     implementation("androidx.appcompat:appcompat:1.1.0")
 
     // Guava
-    implementation(GUAVA_ANDROID)
+    implementation(libs.guavaAndroid)
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.espressoCore)
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestCompileOnly(project(":camera:camera-extensions-stub"))
 
diff --git a/camera/integration-tests/extensionstestlib/build.gradle b/camera/integration-tests/extensionstestlib/build.gradle
index ea4767e..deb9bbd 100644
--- a/camera/integration-tests/extensionstestlib/build.gradle
+++ b/camera/integration-tests/extensionstestlib/build.gradle
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryVersions
+
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
diff --git a/camera/integration-tests/timingtestapp/build.gradle b/camera/integration-tests/timingtestapp/build.gradle
index db40530..73186e7 100644
--- a/camera/integration-tests/timingtestapp/build.gradle
+++ b/camera/integration-tests/timingtestapp/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.CONSTRAINT_LAYOUT
 
 plugins {
     id("AndroidXPlugin")
@@ -64,18 +64,18 @@
     implementation("androidx.exifinterface:exifinterface:1.0.0")
     implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.2.0")
     implementation(CONSTRAINT_LAYOUT, { transitive = true })
-    implementation(KOTLIN_STDLIB)
-    implementation(KOTLIN_COROUTINES_ANDROID)
+    implementation(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesAndroid)
 
     // Testing framework
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE)
-    implementation(ESPRESSO_IDLING_RESOURCE)
-    implementation(TRUTH)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.espressoCore)
+    implementation(libs.espressoIdlingResource)
+    implementation(libs.truth)
 
     // Statistics library
-    implementation(GUAVA_ANDROID)
+    implementation(libs.guavaAndroid)
 }
diff --git a/camera/integration-tests/uiwidgetstestapp/build.gradle b/camera/integration-tests/uiwidgetstestapp/build.gradle
index e52e977..ad5c90b 100644
--- a/camera/integration-tests/uiwidgetstestapp/build.gradle
+++ b/camera/integration-tests/uiwidgetstestapp/build.gradle
@@ -13,9 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -52,7 +49,7 @@
 
 dependencies {
     // Internal library
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation(project(":camera:camera-camera2"))
     implementation(project(":camera:camera-lifecycle"))
     implementation(project(":camera:camera-view"))
@@ -62,16 +59,16 @@
     implementation("androidx.viewpager2:viewpager2:1.0.0")
 
     // Testing framework
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.espressoCore)
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(project(":internal-testutils-runtime"))
-    androidTestImplementation(TRUTH)
-    debugImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(libs.truth)
+    debugImplementation(libs.testCore)
     debugImplementation("androidx.fragment:fragment-testing:1.2.5")
     // Testing resource dependency for manifest
     debugImplementation(project(":camera:camera-testing"))
diff --git a/camera/integration-tests/viewtestapp/build.gradle b/camera/integration-tests/viewtestapp/build.gradle
index 689feab..295b9a4 100644
--- a/camera/integration-tests/viewtestapp/build.gradle
+++ b/camera/integration-tests/viewtestapp/build.gradle
@@ -14,9 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -57,7 +54,7 @@
     implementation("androidx.camera:camera-lifecycle:${VIEW_ATOMIC_GROUP_PINNED_VER}")
     implementation(project(":lifecycle:lifecycle-runtime"))
     implementation(project(":camera:camera-view"))
-    implementation(GUAVA_ANDROID)
+    implementation(libs.guavaAndroid)
     implementation("androidx.exifinterface:exifinterface:1.3.2")
 
     // Lifecycle and LiveData
@@ -67,17 +64,17 @@
     implementation("androidx.appcompat:appcompat:1.1.0")
 
     // Testing framework
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
-    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(libs.espressoCore)
     androidTestImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     androidTestImplementation(project(":lifecycle:lifecycle-runtime"))
     androidTestImplementation("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0")
-    androidTestImplementation(TRUTH)
-    debugImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(libs.truth)
+    debugImplementation(libs.testCore)
     debugImplementation("androidx.fragment:fragment-testing:1.2.3")
     // camera-testing added as 'implementation' dependency to include camera-testing activity in APK
     debugImplementation(project(":camera:camera-testing")) {
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index 5ee2e66..e1ea9bb 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -118,6 +118,14 @@
     }
 
     @Test
+    fun controllerBound_canGetCameraControl() {
+        fragment.assertPreviewIsStreaming()
+        instrumentation.runOnMainSync {
+            assertThat(fragment.cameraController.cameraControl).isNotNull()
+        }
+    }
+
+    @Test
     fun controllerBound_canGetCameraInfo() {
         fragment.assertPreviewIsStreaming()
         instrumentation.runOnMainSync {
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
index 46cb4308..9fab3a8f 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
@@ -51,7 +51,7 @@
 import androidx.camera.view.CameraController;
 import androidx.camera.view.LifecycleCameraController;
 import androidx.camera.view.PreviewView;
-import androidx.camera.view.SensorRotationListener;
+import androidx.camera.view.RotationReceiver;
 import androidx.camera.view.video.ExperimentalVideo;
 import androidx.camera.view.video.OnVideoSavedCallback;
 import androidx.camera.view.video.OutputFileOptions;
@@ -89,7 +89,7 @@
     private ToggleButton mTapToFocusToggle;
     private TextView mZoomStateText;
     private TextView mTorchStateText;
-    private RotationListener mSensorRotationListener;
+    private SensorRotationReceiver mSensorRotationReceiver;
     private TextView mLuminance;
     private boolean mIsAnalyzerSet = true;
 
@@ -123,8 +123,8 @@
             @Nullable ViewGroup container,
             @Nullable Bundle savedInstanceState) {
         mExecutorService = Executors.newSingleThreadExecutor();
-        mSensorRotationListener = new RotationListener(requireContext());
-        mSensorRotationListener.enable();
+        mSensorRotationReceiver = new SensorRotationReceiver(requireContext());
+        mSensorRotationReceiver.enable();
         mCameraController = new LifecycleCameraController(requireContext());
         checkFailedFuture(mCameraController.getInitializationFuture());
         runSafely(() -> mCameraController.bindToLifecycle(getViewLifecycleOwner()));
@@ -321,8 +321,8 @@
         if (mExecutorService != null) {
             mExecutorService.shutdown();
         }
-        if (mSensorRotationListener != null) {
-            mSensorRotationListener.disable();
+        if (mSensorRotationReceiver != null) {
+            mSensorRotationReceiver.disable();
         }
     }
 
@@ -451,11 +451,11 @@
     /**
      * Listens to accelerometer rotation change and pass it to tests.
      */
-    static class RotationListener extends SensorRotationListener {
+    static class SensorRotationReceiver extends RotationReceiver {
 
         private int mRotation;
 
-        RotationListener(@NonNull Context context) {
+        SensorRotationReceiver(@NonNull Context context) {
             super(context);
         }
 
@@ -498,7 +498,7 @@
      */
     @RestrictTo(RestrictTo.Scope.TESTS)
     int getSensorRotation() {
-        return mSensorRotationListener.getRotation();
+        return mSensorRotationReceiver.getRotation();
     }
 
     @VisibleForTesting
diff --git a/car/app/app-automotive/src/androidTest/AndroidManifest.xml b/car/app/app-automotive/src/androidTest/AndroidManifest.xml
index f5f2ff9..db2ba14 100644
--- a/car/app/app-automotive/src/androidTest/AndroidManifest.xml
+++ b/car/app/app-automotive/src/androidTest/AndroidManifest.xml
@@ -15,6 +15,6 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app.activity.test">
+    package="androidx.car.app.automotive.test">
 
 </manifest>
diff --git a/car/app/app-automotive/src/main/AndroidManifest.xml b/car/app/app-automotive/src/main/AndroidManifest.xml
index 3a91379..0e5d23c 100644
--- a/car/app/app-automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-automotive/src/main/AndroidManifest.xml
@@ -15,7 +15,7 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app"
+    package="androidx.car.app.automotive"
     android:versionCode="1"
     android:versionName="0.1">
     <queries>
@@ -30,7 +30,7 @@
     -->
     <application>
         <activity
-            android:name=".activity.CarAppActivity"
+            android:name="androidx.car.app.activity.CarAppActivity"
             android:theme="@android:style/Theme.NoTitleBar"
             android:exported="false"
             android:windowSoftInputMode="adjustResize">
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
index 3171fb1..e981329 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
@@ -32,7 +32,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
-import androidx.car.app.R;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IRendererCallback;
 import androidx.car.app.activity.renderer.IRendererService;
@@ -41,6 +40,7 @@
 import androidx.car.app.activity.renderer.surface.SurfaceHolderListener;
 import androidx.car.app.activity.renderer.surface.SurfaceWrapperProvider;
 import androidx.car.app.activity.renderer.surface.TemplateSurfaceView;
+import androidx.car.app.automotive.R;
 import androidx.car.app.serialization.Bundleable;
 import androidx.car.app.serialization.BundlerException;
 import androidx.car.app.utils.ThreadUtils;
@@ -84,9 +84,14 @@
  * <p>Note the name of the alias should be unique and resemble a fully qualified class name, but
  * unlike the name of the target activity, the alias name is arbitrary; it does not refer to an
  * actual class.
+ *
+ * <h4>Distraction-optimized Activities</h4>
+ *
+ * <p>The activity must be the {@code distractionOptimized} meta-data set to {@code true}, in order
+ * for it to be displayed while driving. This is the only activity that can have this meta-data
+ * set to {@code true}, any other activities marked this way may cause the app to be rejected
+ * during app submission.
  */
-// TODO(b/179225768): Remove distractionOptimized from the javadoc above if we can make that
-// implicit for car apps.
 @SuppressLint({"ForbiddenSuperClass"})
 public final class CarAppActivity extends FragmentActivity {
     @VisibleForTesting
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorHandler.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorHandler.java
index 9fa3081..cc3ce4b 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorHandler.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorHandler.java
@@ -21,7 +21,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringRes;
-import androidx.car.app.R;
+import androidx.car.app.automotive.R;
 
 /**
  * Error handling abstraction
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorMessageFragment.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorMessageFragment.java
index c66cc1a..502e337 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorMessageFragment.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ErrorMessageFragment.java
@@ -33,7 +33,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
-import androidx.car.app.R;
+import androidx.car.app.automotive.R;
 import androidx.fragment.app.Fragment;
 
 import java.util.List;
diff --git a/car/app/app-automotive/src/test/AndroidManifest.xml b/car/app/app-automotive/src/test/AndroidManifest.xml
index d66221d..8ab2286 100644
--- a/car/app/app-automotive/src/test/AndroidManifest.xml
+++ b/car/app/app-automotive/src/test/AndroidManifest.xml
@@ -15,5 +15,5 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app">
+    package="androidx.car.app.automotive">
 </manifest>
diff --git a/car/app/app-projected/src/main/AndroidManifest.xml b/car/app/app-projected/src/main/AndroidManifest.xml
index 9dcedf7..ced5923 100644
--- a/car/app/app-projected/src/main/AndroidManifest.xml
+++ b/car/app/app-projected/src/main/AndroidManifest.xml
@@ -15,6 +15,6 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.car.app">
+    package="androidx.car.app.projected">
 
 </manifest>
\ No newline at end of file
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
index 100a797..860446f 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
@@ -16,12 +16,15 @@
 
 package androidx.car.app.sample.showcase.common.templates;
 
+import static androidx.car.app.CarToast.LENGTH_LONG;
 import static androidx.car.app.model.Action.BACK;
 
 import androidx.annotation.NonNull;
 import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
 import androidx.car.app.Screen;
 import androidx.car.app.model.Action;
+import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarColor;
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.MessageTemplate;
@@ -60,6 +63,21 @@
                                             throw new RuntimeException("Error");
                                         })
                                 .build())
+
+                .setActionStrip(
+                        new ActionStrip.Builder()
+                                .addAction(
+                                        new Action.Builder()
+                                                .setTitle("Settings")
+                                                .setOnClickListener(
+                                                        () ->
+                                                                CarToast.makeText(
+                                                                        getCarContext(),
+                                                                        "Clicked Settings",
+                                                                        LENGTH_LONG)
+                                                                        .show())
+                                                .build())
+                                .build())
                 .build();
     }
 }
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
index 365b683..a545dcd 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
@@ -260,6 +260,8 @@
 //                        .setClass(getCarContext(), SignInWithGoogleActivity.class)
 //                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
 //                        .putExtras(extras));
+        CarToast.makeText(getCarContext(), "Sign-in with Google starts here", LENGTH_LONG)
+                .show();
     }
 
     private MessageTemplate getSignInCompletedMessageTemplate() {
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 3a71b2b..03237cc 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -55,6 +55,7 @@
     field public static final String CAR_SERVICE = "car";
     field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
     field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+    field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
     field public static final String NAVIGATION_SERVICE = "navigation";
     field public static final String SCREEN_SERVICE = "screen";
   }
@@ -189,16 +190,6 @@
     field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
   }
 
-  @Deprecated public final class ConnectionToCar {
-    ctor @Deprecated public ConnectionToCar(android.content.Context);
-    method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
-    field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
-    field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
-    field @Deprecated public static final int NATIVE = 1; // 0x1
-    field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
-    field @Deprecated public static final int PROJECTION = 2; // 0x2
-  }
-
 }
 
 package androidx.car.app.constraints {
@@ -214,6 +205,285 @@
 
 }
 
+package androidx.car.app.hardware {
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager {
+    method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+    method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+  }
+
+}
+
+package androidx.car.app.hardware.common {
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+    field public static final int KILOMETER = 3; // 0x3
+    field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+    field public static final int METER = 2; // 0x2
+    field public static final int METERS_PER_SEC = 101; // 0x65
+    field public static final int MILE = 4; // 0x4
+    field public static final int MILES_PER_HOUR = 103; // 0x67
+    field public static final int MILLIMETER = 1; // 0x1
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+    ctor public CarValue(T?, long, int);
+    method public int getStatus();
+    method public long getTimestampMillis();
+    method public T? getValue();
+    field public static final int STATUS_SUCCESS = 1; // 0x1
+    field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataListener<T> {
+    method public void onCarData(T);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class UpdateRate {
+    field public static final int DEFAULT = 0; // 0x0
+    field public static final int FASTEST = 2; // 0x2
+    field public static final int UI = 1; // 0x1
+  }
+
+}
+
+package androidx.car.app.hardware.info {
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getAccelerometer();
+  }
+
+  public static final class Accelerometer.Builder {
+    ctor public Accelerometer.Builder();
+    method public androidx.car.app.hardware.info.Accelerometer build();
+    method public androidx.car.app.hardware.info.Accelerometer.Builder setAccelerometer(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Accelerometer.Params {
+    ctor public Accelerometer.Params(int);
+    method public static androidx.car.app.hardware.info.Accelerometer.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+    method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+  }
+
+  public static final class CarHardwareLocation.Builder {
+    ctor public CarHardwareLocation.Builder();
+    method public androidx.car.app.hardware.info.CarHardwareLocation build();
+    method public androidx.car.app.hardware.info.CarHardwareLocation.Builder setLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+  }
+
+  public static final class CarHardwareLocation.Params {
+    ctor public CarHardwareLocation.Params(int);
+    method public static androidx.car.app.hardware.info.CarHardwareLocation.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+    method public void addEnergyLevelListener(androidx.car.app.hardware.info.EnergyLevel.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+    method public void addMileageListener(androidx.car.app.hardware.info.Mileage.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+    method public void addSpeedListener(androidx.car.app.hardware.info.Speed.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+    method public void addTollListener(androidx.car.app.hardware.info.Toll.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+    method public void getEnergyProfile(androidx.car.app.hardware.info.EnergyProfile.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyProfile!>);
+    method public void getModel(androidx.car.app.hardware.info.Model.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Model!>);
+    method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+    method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+    method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+    method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+  }
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+    method public void addAccelerometerListener(androidx.car.app.hardware.info.Accelerometer.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+    method public void addCarHardwareLocationListener(androidx.car.app.hardware.info.CarHardwareLocation.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+    method public void addCompassListener(androidx.car.app.hardware.info.Compass.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+    method public void addGyroscopeListener(androidx.car.app.hardware.info.Gyroscope.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+    method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+    method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+    method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+    method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getCompass();
+  }
+
+  public static final class Compass.Builder {
+    ctor public Compass.Builder();
+    method public androidx.car.app.hardware.info.Compass build();
+    method public androidx.car.app.hardware.info.Compass.Builder setCompass(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Compass.Params {
+    ctor public Compass.Params(int);
+    method public static androidx.car.app.hardware.info.Compass.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemaining();
+  }
+
+  public static final class EnergyLevel.Builder {
+    ctor public EnergyLevel.Builder();
+    method public androidx.car.app.hardware.info.EnergyLevel build();
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemaining(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+  }
+
+  public static final class EnergyLevel.Params {
+    ctor public EnergyLevel.Params(int);
+    method public static androidx.car.app.hardware.info.EnergyLevel.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getEvConnectorTypes();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getFuelTypes();
+    field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+    field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+    field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+    field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+    field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+    field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+    field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+    field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+    field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+    field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+    field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+    field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+    field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+    field public static final int FUEL_TYPE_CNG = 8; // 0x8
+    field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+    field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+    field public static final int FUEL_TYPE_E85 = 6; // 0x6
+    field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+    field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+    field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+    field public static final int FUEL_TYPE_LNG = 9; // 0x9
+    field public static final int FUEL_TYPE_LPG = 7; // 0x7
+    field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+    field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+  }
+
+  public static final class EnergyProfile.Builder {
+    ctor public EnergyProfile.Builder();
+    method public androidx.car.app.hardware.info.EnergyProfile build();
+    method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+    method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+  }
+
+  public static final class EnergyProfile.Params {
+    ctor public EnergyProfile.Params();
+    method public static androidx.car.app.hardware.info.EnergyProfile.Params getDefault();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getGyroscope();
+  }
+
+  public static final class Gyroscope.Builder {
+    ctor public Gyroscope.Builder();
+    method public androidx.car.app.hardware.info.Gyroscope build();
+    method public androidx.car.app.hardware.info.Gyroscope.Builder setGyroscope(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Gyroscope.Params {
+    ctor public Gyroscope.Params(int);
+    method public static androidx.car.app.hardware.info.Gyroscope.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometer();
+  }
+
+  public static final class Mileage.Builder {
+    ctor public Mileage.Builder();
+    method public androidx.car.app.hardware.info.Mileage build();
+    method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+    method public androidx.car.app.hardware.info.Mileage.Builder setOdometer(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+  }
+
+  public static final class Mileage.Params {
+    ctor public Mileage.Params(int);
+    method public static androidx.car.app.hardware.info.Mileage.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+  }
+
+  public static final class Model.Builder {
+    ctor public Model.Builder();
+    method public androidx.car.app.hardware.info.Model build();
+    method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+    method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+    method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Model.Params {
+    ctor public Model.Params();
+    method public static androidx.car.app.hardware.info.Model.Params getDefault();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeed();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeed();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+  }
+
+  public static final class Speed.Builder {
+    ctor public Speed.Builder();
+    method public androidx.car.app.hardware.info.Speed build();
+    method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.Speed.Builder setRawSpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Speed.Params {
+    ctor public Speed.Params(int);
+    method public static androidx.car.app.hardware.info.Speed.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Toll {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+    field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+    field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+    field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+    field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+  }
+
+  public static final class Toll.Builder {
+    ctor public Toll.Builder();
+    method public androidx.car.app.hardware.info.Toll build();
+    method public androidx.car.app.hardware.info.Toll.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Toll.Params {
+    ctor public Toll.Params();
+    method public static androidx.car.app.hardware.info.Toll.Params getDefault();
+  }
+
+}
+
 package androidx.car.app.model {
 
   @androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index dbcb362..a91d6a4 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -55,6 +55,7 @@
     field public static final String CAR_SERVICE = "car";
     field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
     field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+    field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
     field public static final String NAVIGATION_SERVICE = "navigation";
     field public static final String SCREEN_SERVICE = "screen";
   }
@@ -192,16 +193,6 @@
     field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
   }
 
-  @Deprecated public final class ConnectionToCar {
-    ctor @Deprecated public ConnectionToCar(android.content.Context);
-    method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
-    field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
-    field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
-    field @Deprecated public static final int NATIVE = 1; // 0x1
-    field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
-    field @Deprecated public static final int PROJECTION = 2; // 0x2
-  }
-
 }
 
 package androidx.car.app.constraints {
@@ -217,6 +208,285 @@
 
 }
 
+package androidx.car.app.hardware {
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager {
+    method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+    method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+  }
+
+}
+
+package androidx.car.app.hardware.common {
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+    field public static final int KILOMETER = 3; // 0x3
+    field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+    field public static final int METER = 2; // 0x2
+    field public static final int METERS_PER_SEC = 101; // 0x65
+    field public static final int MILE = 4; // 0x4
+    field public static final int MILES_PER_HOUR = 103; // 0x67
+    field public static final int MILLIMETER = 1; // 0x1
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+    ctor public CarValue(T?, long, int);
+    method public int getStatus();
+    method public long getTimestampMillis();
+    method public T? getValue();
+    field public static final int STATUS_SUCCESS = 1; // 0x1
+    field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataListener<T> {
+    method public void onCarData(T);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class UpdateRate {
+    field public static final int DEFAULT = 0; // 0x0
+    field public static final int FASTEST = 2; // 0x2
+    field public static final int UI = 1; // 0x1
+  }
+
+}
+
+package androidx.car.app.hardware.info {
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getAccelerometer();
+  }
+
+  public static final class Accelerometer.Builder {
+    ctor public Accelerometer.Builder();
+    method public androidx.car.app.hardware.info.Accelerometer build();
+    method public androidx.car.app.hardware.info.Accelerometer.Builder setAccelerometer(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Accelerometer.Params {
+    ctor public Accelerometer.Params(int);
+    method public static androidx.car.app.hardware.info.Accelerometer.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+    method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+  }
+
+  public static final class CarHardwareLocation.Builder {
+    ctor public CarHardwareLocation.Builder();
+    method public androidx.car.app.hardware.info.CarHardwareLocation build();
+    method public androidx.car.app.hardware.info.CarHardwareLocation.Builder setLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+  }
+
+  public static final class CarHardwareLocation.Params {
+    ctor public CarHardwareLocation.Params(int);
+    method public static androidx.car.app.hardware.info.CarHardwareLocation.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+    method public void addEnergyLevelListener(androidx.car.app.hardware.info.EnergyLevel.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+    method public void addMileageListener(androidx.car.app.hardware.info.Mileage.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+    method public void addSpeedListener(androidx.car.app.hardware.info.Speed.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+    method public void addTollListener(androidx.car.app.hardware.info.Toll.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+    method public void getEnergyProfile(androidx.car.app.hardware.info.EnergyProfile.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyProfile!>);
+    method public void getModel(androidx.car.app.hardware.info.Model.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Model!>);
+    method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+    method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+    method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+    method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+  }
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+    method public void addAccelerometerListener(androidx.car.app.hardware.info.Accelerometer.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+    method public void addCarHardwareLocationListener(androidx.car.app.hardware.info.CarHardwareLocation.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+    method public void addCompassListener(androidx.car.app.hardware.info.Compass.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+    method public void addGyroscopeListener(androidx.car.app.hardware.info.Gyroscope.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+    method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+    method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+    method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+    method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getCompass();
+  }
+
+  public static final class Compass.Builder {
+    ctor public Compass.Builder();
+    method public androidx.car.app.hardware.info.Compass build();
+    method public androidx.car.app.hardware.info.Compass.Builder setCompass(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Compass.Params {
+    ctor public Compass.Params(int);
+    method public static androidx.car.app.hardware.info.Compass.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemaining();
+  }
+
+  public static final class EnergyLevel.Builder {
+    ctor public EnergyLevel.Builder();
+    method public androidx.car.app.hardware.info.EnergyLevel build();
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemaining(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+  }
+
+  public static final class EnergyLevel.Params {
+    ctor public EnergyLevel.Params(int);
+    method public static androidx.car.app.hardware.info.EnergyLevel.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getEvConnectorTypes();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getFuelTypes();
+    field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+    field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+    field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+    field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+    field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+    field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+    field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+    field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+    field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+    field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+    field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+    field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+    field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+    field public static final int FUEL_TYPE_CNG = 8; // 0x8
+    field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+    field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+    field public static final int FUEL_TYPE_E85 = 6; // 0x6
+    field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+    field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+    field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+    field public static final int FUEL_TYPE_LNG = 9; // 0x9
+    field public static final int FUEL_TYPE_LPG = 7; // 0x7
+    field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+    field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+  }
+
+  public static final class EnergyProfile.Builder {
+    ctor public EnergyProfile.Builder();
+    method public androidx.car.app.hardware.info.EnergyProfile build();
+    method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+    method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+  }
+
+  public static final class EnergyProfile.Params {
+    ctor public EnergyProfile.Params();
+    method public static androidx.car.app.hardware.info.EnergyProfile.Params getDefault();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getGyroscope();
+  }
+
+  public static final class Gyroscope.Builder {
+    ctor public Gyroscope.Builder();
+    method public androidx.car.app.hardware.info.Gyroscope build();
+    method public androidx.car.app.hardware.info.Gyroscope.Builder setGyroscope(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Gyroscope.Params {
+    ctor public Gyroscope.Params(int);
+    method public static androidx.car.app.hardware.info.Gyroscope.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometer();
+  }
+
+  public static final class Mileage.Builder {
+    ctor public Mileage.Builder();
+    method public androidx.car.app.hardware.info.Mileage build();
+    method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+    method public androidx.car.app.hardware.info.Mileage.Builder setOdometer(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+  }
+
+  public static final class Mileage.Params {
+    ctor public Mileage.Params(int);
+    method public static androidx.car.app.hardware.info.Mileage.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+  }
+
+  public static final class Model.Builder {
+    ctor public Model.Builder();
+    method public androidx.car.app.hardware.info.Model build();
+    method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+    method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+    method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Model.Params {
+    ctor public Model.Params();
+    method public static androidx.car.app.hardware.info.Model.Params getDefault();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeed();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeed();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+  }
+
+  public static final class Speed.Builder {
+    ctor public Speed.Builder();
+    method public androidx.car.app.hardware.info.Speed build();
+    method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.Speed.Builder setRawSpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Speed.Params {
+    ctor public Speed.Params(int);
+    method public static androidx.car.app.hardware.info.Speed.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Toll {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+    field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+    field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+    field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+    field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+  }
+
+  public static final class Toll.Builder {
+    ctor public Toll.Builder();
+    method public androidx.car.app.hardware.info.Toll build();
+    method public androidx.car.app.hardware.info.Toll.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Toll.Params {
+    ctor public Toll.Params();
+    method public static androidx.car.app.hardware.info.Toll.Params getDefault();
+  }
+
+}
+
 package androidx.car.app.model {
 
   @androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 3a71b2b..03237cc 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -55,6 +55,7 @@
     field public static final String CAR_SERVICE = "car";
     field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
     field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+    field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
     field public static final String NAVIGATION_SERVICE = "navigation";
     field public static final String SCREEN_SERVICE = "screen";
   }
@@ -189,16 +190,6 @@
     field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
   }
 
-  @Deprecated public final class ConnectionToCar {
-    ctor @Deprecated public ConnectionToCar(android.content.Context);
-    method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
-    field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
-    field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
-    field @Deprecated public static final int NATIVE = 1; // 0x1
-    field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
-    field @Deprecated public static final int PROJECTION = 2; // 0x2
-  }
-
 }
 
 package androidx.car.app.constraints {
@@ -214,6 +205,285 @@
 
 }
 
+package androidx.car.app.hardware {
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager {
+    method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+    method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+  }
+
+}
+
+package androidx.car.app.hardware.common {
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+    field public static final int KILOMETER = 3; // 0x3
+    field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+    field public static final int METER = 2; // 0x2
+    field public static final int METERS_PER_SEC = 101; // 0x65
+    field public static final int MILE = 4; // 0x4
+    field public static final int MILES_PER_HOUR = 103; // 0x67
+    field public static final int MILLIMETER = 1; // 0x1
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+    ctor public CarValue(T?, long, int);
+    method public int getStatus();
+    method public long getTimestampMillis();
+    method public T? getValue();
+    field public static final int STATUS_SUCCESS = 1; // 0x1
+    field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataListener<T> {
+    method public void onCarData(T);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class UpdateRate {
+    field public static final int DEFAULT = 0; // 0x0
+    field public static final int FASTEST = 2; // 0x2
+    field public static final int UI = 1; // 0x1
+  }
+
+}
+
+package androidx.car.app.hardware.info {
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getAccelerometer();
+  }
+
+  public static final class Accelerometer.Builder {
+    ctor public Accelerometer.Builder();
+    method public androidx.car.app.hardware.info.Accelerometer build();
+    method public androidx.car.app.hardware.info.Accelerometer.Builder setAccelerometer(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Accelerometer.Params {
+    ctor public Accelerometer.Params(int);
+    method public static androidx.car.app.hardware.info.Accelerometer.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+    method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+  }
+
+  public static final class CarHardwareLocation.Builder {
+    ctor public CarHardwareLocation.Builder();
+    method public androidx.car.app.hardware.info.CarHardwareLocation build();
+    method public androidx.car.app.hardware.info.CarHardwareLocation.Builder setLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+  }
+
+  public static final class CarHardwareLocation.Params {
+    ctor public CarHardwareLocation.Params(int);
+    method public static androidx.car.app.hardware.info.CarHardwareLocation.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+    method public void addEnergyLevelListener(androidx.car.app.hardware.info.EnergyLevel.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+    method public void addMileageListener(androidx.car.app.hardware.info.Mileage.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+    method public void addSpeedListener(androidx.car.app.hardware.info.Speed.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+    method public void addTollListener(androidx.car.app.hardware.info.Toll.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+    method public void getEnergyProfile(androidx.car.app.hardware.info.EnergyProfile.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyProfile!>);
+    method public void getModel(androidx.car.app.hardware.info.Model.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Model!>);
+    method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+    method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+    method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+    method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+  }
+
+  @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+    method public void addAccelerometerListener(androidx.car.app.hardware.info.Accelerometer.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+    method public void addCarHardwareLocationListener(androidx.car.app.hardware.info.CarHardwareLocation.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+    method public void addCompassListener(androidx.car.app.hardware.info.Compass.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+    method public void addGyroscopeListener(androidx.car.app.hardware.info.Gyroscope.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+    method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+    method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+    method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+    method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getCompass();
+  }
+
+  public static final class Compass.Builder {
+    ctor public Compass.Builder();
+    method public androidx.car.app.hardware.info.Compass build();
+    method public androidx.car.app.hardware.info.Compass.Builder setCompass(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Compass.Params {
+    ctor public Compass.Params(int);
+    method public static androidx.car.app.hardware.info.Compass.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemaining();
+  }
+
+  public static final class EnergyLevel.Builder {
+    ctor public EnergyLevel.Builder();
+    method public androidx.car.app.hardware.info.EnergyLevel build();
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemaining(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+  }
+
+  public static final class EnergyLevel.Params {
+    ctor public EnergyLevel.Params(int);
+    method public static androidx.car.app.hardware.info.EnergyLevel.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getEvConnectorTypes();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getFuelTypes();
+    field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+    field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+    field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+    field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+    field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+    field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+    field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+    field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+    field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+    field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+    field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+    field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+    field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+    field public static final int FUEL_TYPE_CNG = 8; // 0x8
+    field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+    field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+    field public static final int FUEL_TYPE_E85 = 6; // 0x6
+    field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+    field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+    field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+    field public static final int FUEL_TYPE_LNG = 9; // 0x9
+    field public static final int FUEL_TYPE_LPG = 7; // 0x7
+    field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+    field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+  }
+
+  public static final class EnergyProfile.Builder {
+    ctor public EnergyProfile.Builder();
+    method public androidx.car.app.hardware.info.EnergyProfile build();
+    method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+    method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+  }
+
+  public static final class EnergyProfile.Params {
+    ctor public EnergyProfile.Params();
+    method public static androidx.car.app.hardware.info.EnergyProfile.Params getDefault();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getGyroscope();
+  }
+
+  public static final class Gyroscope.Builder {
+    ctor public Gyroscope.Builder();
+    method public androidx.car.app.hardware.info.Gyroscope build();
+    method public androidx.car.app.hardware.info.Gyroscope.Builder setGyroscope(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+  }
+
+  public static final class Gyroscope.Params {
+    ctor public Gyroscope.Params(int);
+    method public static androidx.car.app.hardware.info.Gyroscope.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometer();
+  }
+
+  public static final class Mileage.Builder {
+    ctor public Mileage.Builder();
+    method public androidx.car.app.hardware.info.Mileage build();
+    method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+    method public androidx.car.app.hardware.info.Mileage.Builder setOdometer(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+  }
+
+  public static final class Mileage.Params {
+    ctor public Mileage.Params(int);
+    method public static androidx.car.app.hardware.info.Mileage.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+  }
+
+  public static final class Model.Builder {
+    ctor public Model.Builder();
+    method public androidx.car.app.hardware.info.Model build();
+    method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+    method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+    method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Model.Params {
+    ctor public Model.Params();
+    method public static androidx.car.app.hardware.info.Model.Params getDefault();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeed();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeed();
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+  }
+
+  public static final class Speed.Builder {
+    ctor public Speed.Builder();
+    method public androidx.car.app.hardware.info.Speed build();
+    method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.Speed.Builder setRawSpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+    method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Speed.Params {
+    ctor public Speed.Params(int);
+    method public static androidx.car.app.hardware.info.Speed.Params getDefault();
+    method public int getRate();
+  }
+
+  @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Toll {
+    method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+    field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+    field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+    field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+    field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+  }
+
+  public static final class Toll.Builder {
+    ctor public Toll.Builder();
+    method public androidx.car.app.hardware.info.Toll build();
+    method public androidx.car.app.hardware.info.Toll.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+  }
+
+  public static final class Toll.Params {
+    ctor public Toll.Params();
+    method public static androidx.car.app.hardware.info.Toll.Params getDefault();
+  }
+
+}
+
 package androidx.car.app.model {
 
   @androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/src/main/java/androidx/car/app/CarContext.java b/car/app/app/src/main/java/androidx/car/app/CarContext.java
index 3e97f76..2220466 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarContext.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarContext.java
@@ -39,10 +39,12 @@
 import androidx.activity.OnBackPressedDispatcher;
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringDef;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.hardware.CarHardwareManager;
 import androidx.car.app.navigation.NavigationManager;
 import androidx.car.app.notification.CarPendingIntent;
 import androidx.car.app.utils.RemoteUtils;
@@ -93,7 +95,8 @@
      *
      * @hide
      */
-    @StringDef({APP_SERVICE, CAR_SERVICE, NAVIGATION_SERVICE, SCREEN_SERVICE, CONSTRAINT_SERVICE})
+    @StringDef({APP_SERVICE, CAR_SERVICE, NAVIGATION_SERVICE, SCREEN_SERVICE, CONSTRAINT_SERVICE,
+            HARDWARE_SERVICE})
     @Retention(RetentionPolicy.SOURCE)
     @RestrictTo(LIBRARY)
     public @interface CarServiceType {
@@ -120,6 +123,10 @@
      */
     public static final String CAR_SERVICE = "car";
 
+    /** Manages access to androidx.car.app.hardware properties, sensors and actions. */
+    @RequiresCarApi(3)
+    public static final String HARDWARE_SERVICE = "hardware";
+
     /**
      * Key for including a IStartCarApp in the notification {@link Intent}, for starting the app
      * if it has not been opened yet.
@@ -151,10 +158,18 @@
     static final String EXTRA_ON_REQUEST_PERMISSIONS_RESULT_CALLBACK_KEY =
             "androidx.car.app.action.EXTRA_ON_REQUEST_PERMISSIONS_RESULT_CALLBACK_KEY";
 
+    /**
+     * Holds an exception to be thrown when accessing {@link CarHardwareManager} if there is an
+     * error during initialization.
+     */
+    @Nullable
+    private final IllegalStateException mCarHardwareManagerException;
+
     private final AppManager mAppManager;
     private final NavigationManager mNavigationManager;
     private final ScreenManager mScreenManager;
     private final ConstraintManager mConstraintManager;
+    private final CarHardwareManager mCarHardwareManager;
     private final OnBackPressedDispatcher mOnBackPressedDispatcher;
     private final HostDispatcher mHostDispatcher;
     private final Lifecycle mLifecycle;
@@ -208,6 +223,11 @@
                 return mScreenManager;
             case CONSTRAINT_SERVICE:
                 return mConstraintManager;
+            case HARDWARE_SERVICE:
+                if (mCarHardwareManagerException != null) {
+                    throw mCarHardwareManagerException;
+                }
+                return mCarHardwareManager;
             default: // fall out
         }
 
@@ -258,6 +278,8 @@
             return SCREEN_SERVICE;
         } else if (serviceClass.isInstance(mConstraintManager)) {
             return CONSTRAINT_SERVICE;
+        } else if (serviceClass.isInstance(mCarHardwareManager)) {
+            return HARDWARE_SERVICE;
         }
 
         throw new IllegalArgumentException("The class does not correspond to a car service");
@@ -450,25 +472,10 @@
     }
 
     /**
-     * Requests the provided {@code permissions} from the user.
+     * Requests the provided {@code permissions} from the user, calling the provided {@code
+     * callback} in the main thread.
      *
-     * <p>When the result is available, the {@code callback} provided will be called on the main
-     * thread.
-     *
-     * <p>This method should be called using a parked only listener.
-     *
-     * <p>If this method is calle while the host deems it is unsafe (for example, when the user
-     * is driving), the permission(s) may not be requested from the user, automatically rejecting
-     * the permissions requested.
-     *
-     * <p>If the Session is destroyed before the user accepts or rejects the permissions, the
-     * callback will not be executed.
-     *
-     * @param permissions the runtime permissions to request from the user
-     * @param callback    callback that will be notified when the user takes action on the
-     *                    permission request
-     * @throws NullPointerException if either {@code permissions} or {@code callback} are {@code
-     *                              null}
+     * @see CarContext#requestPermissions(List, Executor, OnRequestPermissionsCallback)
      */
     public void requestPermissions(@NonNull List<String> permissions,
             @NonNull OnRequestPermissionsCallback callback) {
@@ -481,14 +488,26 @@
      * <p>When the result is available, the {@code callback} provided will be called using the
      * {@link Executor} provided.
      *
-     * <p>This method should be called using a parked only listener.
+     * <p>This method should be called using a
+     * {@link androidx.car.app.model.ParkedOnlyOnClickListener}.
      *
-     * <p>If this method is calle while the host deems it is unsafe (for example, when the user
-     * is driving), the permission(s) may not be requested from the user, automatically rejecting
-     * the permissions requested.
+     * <p>If this method is called while the host deems it is unsafe (for example, when the user
+     * is driving), the permission(s) will not be requested from the user.
      *
-     * <p>If the Session is destroyed before the user accepts or rejects the permissions, the
-     * callback will not be executed.
+     * <p>If the {@link Session} is destroyed before the user accepts or rejects the permissions,
+     * the callback will not be executed.
+     *
+     * <h4>Platform Considerations</h4>
+     *
+     * Using this method allows the app to work across all platforms supported by the library with
+     * the same API (e.g. Android Auto on mobile devices and Android Automotive OS on native car
+     * heads unit). On a mobile platform, this method will start an activity that will display the
+     * platform's permissions UI over it. You can choose to not
+     * use this method and instead implement your own activity and code to request the
+     * permissions in that platform. On Automotive OS however, distraction-optimized activities
+     * other than {@link androidx.car.app.activity.CarAppActivity} are not allowed and may be
+     * rejected during app submission. See {@link androidx.car.app.activity.CarAppActivity} for
+     * more details.
      *
      * @param permissions the runtime permissions to request from the user
      * @param executor    the executor that will be used for calling the {@code callback} provided
@@ -627,6 +646,24 @@
         mNavigationManager = NavigationManager.create(this, hostDispatcher, lifecycle);
         mScreenManager = ScreenManager.create(this, lifecycle);
         mConstraintManager = ConstraintManager.create(this, hostDispatcher);
+
+        // Try to instantiate a CarHardwareManager.
+        CarHardwareManager carHardwareManager = null;
+        IllegalStateException carHardwareManagerException = null;
+        try {
+            carHardwareManager = CarHardwareManager.create(this, hostDispatcher);
+            if (carHardwareManager == null) {
+                carHardwareManagerException = new IllegalStateException("CarHardwareManager not "
+                        + "configured. Did you forget to add a dependency on automotive or "
+                        + "projected artifacts?");
+            }
+        } catch (IllegalStateException e) {
+            carHardwareManager = new CarHardwareManager() { };
+            carHardwareManagerException = e;
+        }
+        mCarHardwareManager = carHardwareManager;
+        mCarHardwareManagerException = carHardwareManagerException;
+
         mOnBackPressedDispatcher =
                 new OnBackPressedDispatcher(() -> getCarService(ScreenManager.class).pop());
         mLifecycle = lifecycle;
diff --git a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java b/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java
deleted file mode 100644
index 4caabbe..0000000
--- a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2021 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.car.app.connection;
-
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import static androidx.car.app.utils.CommonUtils.isAutomotiveOS;
-
-import static java.util.Objects.requireNonNull;
-
-import android.content.Context;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.LiveData;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-// TODO(b/169537526): Remove this class
-/**
- * A class that allows retrieval of information about connection to a car head unit.
- *
- * @deprecated use {@link CarConnection} instead.
- */
-@Deprecated
-public final class ConnectionToCar {
-    /**
-     * Defines current car connection state.
-     *
-     * <p>This is used for communication with the car host's content provider on queries for
-     * connection type.
-     */
-    public static final String CAR_CONNECTION_STATE = "CarConnectionState";
-
-    /**
-     * Broadcast action that notifies that the car connection has changed and needs to be updated.
-     */
-    public static final String ACTION_CAR_CONNECTION_UPDATED =
-            "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
-
-    /**
-     * Represents the types of connections that exist to a car head unit.
-     *
-     * @hide
-     */
-    @IntDef({NOT_CONNECTED, NATIVE, PROJECTION})
-    @Retention(RetentionPolicy.SOURCE)
-    @Target({ElementType.TYPE_USE})
-    @RestrictTo(LIBRARY)
-    public @interface ConnectionType {
-    }
-
-    /**
-     * Not connected to any car head unit.
-     */
-    public static final int NOT_CONNECTED = 0;
-
-    /**
-     * Natively running on a head unit (Android Automotive OS).
-     */
-    public static final int NATIVE = 1;
-
-    /**
-     * Connected to a car head unit by projecting to it.
-     */
-    public static final int PROJECTION = 2;
-
-    private final LiveData<Integer> mConnectionTypeLiveData;
-
-    /**
-     * Constructs a {@link ConnectionToCar} that can be used to get connection information.
-     *
-     * @throws NullPointerException if {@code context} is {@code null}
-     */
-    public ConnectionToCar(@NonNull Context context) {
-        requireNonNull(context);
-        mConnectionTypeLiveData = isAutomotiveOS(context)
-                ? new AutomotiveCarConnectionTypeLiveData()
-                : new CarConnectionTypeLiveData(context);
-    }
-
-    /**
-     * Returns a {@link LiveData} that can be observed to get current connection type.
-     *
-     * <p>The recommended pattern is to observe the {@link LiveData} with the activity's
-     * lifecycle in order to get updates on the state change throughout the activity's lifetime.
-     *
-     * <p>Connection types are:
-     * <ol>
-     *     <li>{@link #NOT_CONNECTED}
-     *     <li>{@link #NATIVE}
-     *     <li>{@link #PROJECTION}
-     * </ol>
-     */
-    @NonNull
-    public LiveData<@ConnectionType Integer> getType() {
-        return mConnectionTypeLiveData;
-    }
-}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/CarHardwareManager.java b/car/app/app/src/main/java/androidx/car/app/hardware/CarHardwareManager.java
new file mode 100644
index 0000000..7825bb3
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/CarHardwareManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 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.car.app.hardware;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.HostDispatcher;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.info.CarInfo;
+import androidx.car.app.hardware.info.CarSensors;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Manages access to car hardware specific properties and sensors.
+ */
+@RequiresCarApi(3)
+public interface CarHardwareManager {
+    /**
+     * Returns the {@link CarInfo} that can be used to query the car hardware information
+     * such as make, model, etc.
+     */
+    default @NonNull CarInfo getCarInfo() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the {@link CarSensors} that can be used to access sensor information from the
+     * car hardware.
+     */
+    default @NonNull CarSensors getCarSensors() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns an instance of {@link CarHardwareManager} depending on linked library including an
+     * optional {@link HostDispatcher}.
+     *
+     * @throws IllegalStateException if none of the supported classes are found or if a supported
+     *                               class was found but the constructor was mismatched
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    @NonNull
+    static CarHardwareManager create(@NonNull Context context,
+            @Nullable HostDispatcher hostDispatcher) throws IllegalStateException {
+
+        try { // Check for automotive library first.
+            Class<?> c = Class.forName("androidx.car.app.hardware.CarHardwareManagerAutomotive");
+            Constructor<?> ctor = c.getConstructor(Context.class);
+            Object object = ctor.newInstance(context);
+            return (CarHardwareManager) object;
+        } catch (ClassNotFoundException e) {
+            // No Automotive. Fall through.
+        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException
+                | InvocationTargetException e) {
+            // Something went wrong with accessing the constructor or calling newInstance().
+            throw new IllegalStateException("Mismatch with app-automotive artifact", e);
+        }
+
+        try { // Check for automotive library first.
+            Class<?> c = Class.forName("androidx.car.app.hardware.CarHardwareManagerProjected");
+            Constructor<?> ctor = c.getConstructor(HostDispatcher.class);
+            Object object = ctor.newInstance(hostDispatcher);
+            return (CarHardwareManager) object;
+        } catch (ClassNotFoundException e) {
+            // No Projected. Fall through.
+        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException
+                | InvocationTargetException e) {
+            // Something went wrong with accessing the constructor or calling newInstance().
+            throw new IllegalStateException("Mismatch with app-projected artifact", e);
+        }
+
+        throw new IllegalStateException("Vehicle Manager not "
+                + "configured. Did you forget to add a dependency on automotive or "
+                + "projected artifacts?");
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java
new file mode 100644
index 0000000..50a8995
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2021 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.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Units such as speed and distance for car hardware measurements and display. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class CarUnit {
+    /**
+     * Defines the possible distance units from car hardware.
+     *
+     * @hide
+     */
+    @IntDef({
+            MILLIMETER,
+            METER,
+            KILOMETER,
+            MILE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface CarDistanceUnit {
+    }
+
+    /** Millimeter unit. */
+    @CarDistanceUnit
+    public static final int MILLIMETER = 1;
+
+    /** Meter unit. */
+    @CarDistanceUnit
+    public static final int METER = 2;
+
+    @CarDistanceUnit
+    /** Kilometer unit. */
+    public static final int KILOMETER = 3;
+
+    /** Miles unit. */
+    @CarDistanceUnit
+    public static final int MILE = 4;
+
+    /**
+     * Defines the possible distance units from car hardware.
+     *
+     * @hide
+     */
+    @IntDef({
+            METERS_PER_SEC,
+            KILOMETERS_PER_HOUR,
+            MILES_PER_HOUR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface CarSpeedUnit {
+    }
+
+    /** Meters per second unit. */
+    @CarSpeedUnit
+    public static final int METERS_PER_SEC = 101;
+
+    /** Kilometers per hour unit. */
+    @CarSpeedUnit
+    public static final int KILOMETERS_PER_HOUR = 102;
+
+    /** Miles per hour unit. */
+    @CarSpeedUnit
+    public static final int MILES_PER_HOUR = 103;
+
+    private CarUnit() {}
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/CarValue.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarValue.java
new file mode 100644
index 0000000..ede436a
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarValue.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2021 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.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A data value object returned from car hardware with associated metadata including status,
+ * timestamp, and the actual value.
+ *
+ * @param <T> data type which is returned by the {@link CarValue}
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class CarValue<T> {
+    /**
+     * Defines the possible status codes when trying to access car hardware properties, sensors,
+     * and actions.
+     *
+     * @hide
+     */
+    @IntDef({
+            STATUS_UNKNOWN,
+            STATUS_SUCCESS,
+            STATUS_UNIMPLEMENTED,
+            STATUS_UNAVAILABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface StatusCode {
+    }
+
+    /**
+     * {@link CarValue} has unknown status.
+     */
+    @StatusCode
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * {@link CarValue} was obtained successfully.
+     */
+    @StatusCode
+    public static final int STATUS_SUCCESS = 1;
+
+    /**
+     * {@link CarValue} attempted for unimplemented property, sensor, or action.
+     *
+     * <p>For example, the car hardware might not be able to return a value such as speed or
+     * energy level and will set the status to this value.
+     */
+    @StatusCode
+    public static final int STATUS_UNIMPLEMENTED = 2;
+
+    /**
+     * {@link CarValue} attempted for unavailable property, sensor, or action.
+     *
+     * <p>For example, the car hardware might not be able to return a value such as climate at the
+     * current time because the engine is off and will set the status to this value.
+     */
+    @StatusCode
+    public static final int STATUS_UNAVAILABLE = 3;
+
+    @Nullable
+    private final T mValue;
+    private final long mTimestampMillis;
+    @StatusCode
+    private final int mStatus;
+
+    private static <T> CarValue<T> unimplemented() {
+        return new CarValue<>(null, 0, CarValue.STATUS_UNIMPLEMENTED);
+    }
+
+    /** @hide */
+    @RestrictTo(LIBRARY)
+    public static final CarValue<Integer> UNIMPLEMENTED_INTEGER = unimplemented();
+
+    /** @hide */
+    @RestrictTo(LIBRARY)
+    public static final CarValue<Boolean> UNIMPLEMENTED_BOOLEAN = unimplemented();
+
+    /** @hide */
+    @RestrictTo(LIBRARY)
+    public static final CarValue<Float> UNIMPLEMENTED_FLOAT = unimplemented();
+
+    /** @hide */
+    @RestrictTo(LIBRARY)
+    public static final CarValue<String> UNIMPLEMENTED_STRING = unimplemented();
+
+    /** @hide */
+    @RestrictTo(LIBRARY)
+    public static final CarValue<Float[]> UNIMPLEMENTED_FLOAT_ARRAY = unimplemented();
+
+    /** @hide */
+    @RestrictTo(LIBRARY)
+    public static final CarValue<Integer[]> UNIMPLEMENTED_INTEGER_ARRAY = unimplemented();
+
+    /**
+     * Returns a the data value or {@code null} if the status is not successful.
+     *
+     * @see #getStatus
+     */
+    @Nullable
+    public T getValue() {
+        return mValue;
+    }
+
+    /**
+     * Returns the time in milliseconds at which the event happened.
+     *
+     * <p>For a given property, sensor, or action, each new value's timestamp should be
+     * monotonically increasing using the same time base as SystemClock.elapsedRealtime().
+     */
+    public long getTimestampMillis() {
+        return mTimestampMillis;
+    }
+
+    /**
+     * Returns the status of this particular result such as success, unavailable, or unimplemented.
+     */
+    @StatusCode
+    public int getStatus() {
+        return mStatus;
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[value: "
+                + mValue
+                + ", timestamp: "
+                + mTimestampMillis
+                + ", Status: "
+                + mStatus
+                + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mValue, mTimestampMillis, mStatus);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof CarValue<?>)) {
+            return false;
+        }
+        CarValue<?> otherCarValue = (CarValue<?>) other;
+        return Objects.equals(mValue, otherCarValue.mValue)
+                && mTimestampMillis == otherCarValue.mTimestampMillis
+                && mStatus == otherCarValue.mStatus;
+    }
+
+    /**
+     * Constructs a new instance of a {@link CarValue}.
+     *
+     * @param value           data to be returned with the result
+     * @param timestampMillis the time in milliseconds when the value was generated. See
+     * {@link #getTimestampMillis}
+     * @param status          the status code associated with this value
+     */
+    public CarValue(@Nullable T value, long timestampMillis, @StatusCode int status) {
+        mValue = value;
+        mTimestampMillis = timestampMillis;
+        mStatus = status;
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private CarValue() {
+        mValue = null;
+        mTimestampMillis = 0;
+        mStatus = STATUS_UNKNOWN;
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/OnCarDataListener.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/OnCarDataListener.java
new file mode 100644
index 0000000..191efff
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/OnCarDataListener.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 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.car.app.hardware.common;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+/**
+ * A listener for data being returned about from the car hardware.
+ *
+ * @param <T> data type returned by the listener
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public interface OnCarDataListener<T> {
+    /**
+     * Notifies that the requested data is available.
+     *
+     * @param data car hardware data that was requested.
+     */
+    void onCarData(@NonNull T data);
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/UpdateRate.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/UpdateRate.java
new file mode 100644
index 0000000..6c27cb7
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/UpdateRate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 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.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Defines the possible update rates that properties, sensors, and actions can be requested with
+ * . */
+@CarProtocol
+@RequiresCarApi(3)
+public final class UpdateRate {
+    /**
+     * Defines the possible update rates that properties, sensors, and actions can be requested
+     * with.
+     *
+     * @hide
+     */
+    @IntDef({
+            DEFAULT,
+            UI,
+            FASTEST,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface Value {
+    }
+
+    /**
+     * Car hardware property, sensor, or action should be fetched at its default rate.
+     */
+    @Value
+    public static final int DEFAULT = 0;
+
+    /**
+     * Car hardware property, sensor, or action should be fetched at a rate consistent with
+     * drawing UI to a screen.
+     */
+    @Value
+    public static final int UI = 1;
+
+    /**
+     * Car hardware property, sensor, or action should be fetched at its fastest possible rate.
+     */
+    @Value
+    public static final int FASTEST = 2;
+
+    private UpdateRate() {}
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Accelerometer.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Accelerometer.java
new file mode 100644
index 0000000..8518764
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Accelerometer.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific accelerometers available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Accelerometer {
+
+    /** Accelerometer request parameters. */
+    public static final class Params {
+        private final @UpdateRate.Value int mRate;
+
+        /**
+         * Construct accelerometer parameter instance.
+         */
+        public Params(@UpdateRate.Value int rate) {
+            mRate = rate;
+        }
+
+        /** Gets the requested data rate for the accelerometer. */
+        public @UpdateRate.Value int getRate() {
+            return mRate;
+        }
+
+        /** Gets an {@link Accelerometer.Params} instance with default values set. */
+        public static @NonNull Accelerometer.Params getDefault() {
+            return new Params(UpdateRate.DEFAULT);
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<Float[]> mAccelerometer;
+
+    /**
+     * Returns the raw accelerometer data from the car sensor.
+     *
+     * <p>Follows the same format as {@link android.hardware.SensorEvent#values}.
+     */
+    @NonNull
+    public CarValue<Float[]> getAccelerometer() {
+        return requireNonNull(mAccelerometer);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ accelerometer: " + mAccelerometer + " ]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAccelerometer);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Accelerometer)) {
+            return false;
+        }
+        Accelerometer otherAccelerometer = (Accelerometer) other;
+
+        return Objects.equals(mAccelerometer, otherAccelerometer.mAccelerometer);
+    }
+
+    Accelerometer(Builder builder) {
+        mAccelerometer = requireNonNull(builder.mAccelerometer);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private Accelerometer() {
+        mAccelerometer = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+    }
+
+    /** A builder of {@link Accelerometer}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Float[]> mAccelerometer;
+
+        /**
+         * Sets the raw accelerometer data.
+         *
+         * @throws NullPointerException if {@code accelerometer} is {@code null}
+         */
+        @NonNull
+        public Builder setAccelerometer(@NonNull CarValue<Float[]> accelerometer) {
+            mAccelerometer = requireNonNull(accelerometer);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link Accelerometer} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public Accelerometer build() {
+            if (mAccelerometer == null) {
+                mAccelerometer = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+            }
+            return new Accelerometer(this);
+        }
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/CarHardwareLocation.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarHardwareLocation.java
new file mode 100644
index 0000000..2f88b62
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarHardwareLocation.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import android.location.Location;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific car location available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class CarHardwareLocation {
+
+    /** {@link CarHardwareLocation} request parameters. */
+    public static final class Params {
+        private final @UpdateRate.Value int mRate;
+
+        /**
+         * Construct car location parameter instance.
+         */
+        public Params(@UpdateRate.Value int rate) {
+            mRate = rate;
+        }
+
+        /** Gets the requested data rate for the location. */
+        public @UpdateRate.Value int getRate() {
+            return mRate;
+        }
+
+        /** Gets an {@link CarHardwareLocation.Params} instance with default values set. */
+        public static @NonNull CarHardwareLocation.Params getDefault() {
+            return new Params(UpdateRate.DEFAULT);
+        }
+    }
+
+    // Not private because needed in builder.
+    static final CarValue<Location> UNIMPLEMENTED_LOCATION = new CarValue<>(null, 0,
+            CarValue.STATUS_UNAVAILABLE);
+
+    @Keep
+    @NonNull
+    private final CarValue<Location> mLocation;
+
+    /** Returns the raw location data from the car sensor. */
+    @NonNull
+    public CarValue<Location> getLocation() {
+        return requireNonNull(mLocation);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ location: " + mLocation + " ]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLocation);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof CarHardwareLocation)) {
+            return false;
+        }
+        CarHardwareLocation otherCarHardwareLocation = (CarHardwareLocation) other;
+
+        return Objects.equals(mLocation, otherCarHardwareLocation.mLocation);
+    }
+
+    CarHardwareLocation(Builder builder) {
+        mLocation = requireNonNull(builder.mLocation);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private CarHardwareLocation() {
+        mLocation = UNIMPLEMENTED_LOCATION;
+    }
+
+    /** A builder of {@link CarHardwareLocation}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Location> mLocation;
+
+        /**
+         * Sets the raw car location data.
+         *
+         * @throws NullPointerException if {@code location} is {@code null}
+         */
+        @NonNull
+        public Builder setLocation(@NonNull CarValue<Location> location) {
+            mLocation = requireNonNull(location);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link CarHardwareLocation} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public CarHardwareLocation build() {
+            if (mLocation == null) {
+                mLocation = UNIMPLEMENTED_LOCATION;
+            }
+            return new CarHardwareLocation(this);
+        }
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/CarInfo.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarInfo.java
new file mode 100644
index 0000000..1f04ce6
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarInfo.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.OnCarDataListener;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages access to car hardware specific info such as model, energy, and speed info.
+ */
+@RequiresCarApi(3)
+public interface CarInfo {
+    /**
+     * Request the {@link Model} information about the car hardware.
+     *
+     * @param params the parameters for this specific request. Use
+     * {@link Model.Params#getDefault()} as a default.
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void getModel(@NonNull Model.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<Model> listener);
+
+    /**
+     * Reguest the {@link EnergyProfile} information about the car hardware.
+     *
+     * @param params the parameters for this request. Use {@link EnergyProfile.Params#getDefault}
+     *              as a default.
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void getEnergyProfile(@NonNull EnergyProfile.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<EnergyProfile> listener);
+
+    /**
+     * Setup an ongoing listener to receive {@link Toll} information from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params the parameters for this request. Use {@link Toll.Params#getDefault}
+     *              as a default.
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void addTollListener(@NonNull Toll.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<Toll> listener);
+
+    /**
+     * Remove an ongoing listener for {@link Toll} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeTollListener(@NonNull OnCarDataListener<Toll> listener);
+
+    /**
+     * Setup an ongoing listener to receive {@link EnergyLevel} information from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params the parameters for this request. Use {@link EnergyLevel.Params#getDefault}
+     *              as a default.
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void addEnergyLevelListener(@NonNull EnergyLevel.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<EnergyLevel> listener);
+
+    /**
+     * Remove an ongoing listener for {@link EnergyLevel} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeEnergyLevelListener(@NonNull OnCarDataListener<EnergyLevel> listener);
+
+    /**
+     * Setup an ongoing listener to receive {@link Speed} information from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params the parameters for this request. Use {@link Speed.Params#getDefault}
+     *              as a default.
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void addSpeedListener(@NonNull Speed.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<Speed> listener);
+
+    /**
+     * Remove an ongoing listener for {@link Speed} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeSpeedListener(@NonNull OnCarDataListener<Speed> listener);
+
+    /**
+     * Setup an ongoing listener to receive {@link Mileage} information from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params the parameters for this request. Use {@link Mileage.Params#getDefault}
+     *              as a default.
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use.
+     */
+    void addMileageListener(@NonNull Mileage.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<Mileage> listener);
+
+    /**
+     * Remove an ongoing listener for {@link Mileage} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeMileageListener(@NonNull OnCarDataListener<Mileage> listener);
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/CarSensors.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarSensors.java
new file mode 100644
index 0000000..3f82d41
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarSensors.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.OnCarDataListener;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages access to androidx.car.app.hardware specific sensors such as compass, accelerometer,
+ * and gyroscope.
+ */
+@RequiresCarApi(3)
+public interface CarSensors {
+    /**
+     * Setup an ongoing listener to receive {@link Accelerometer} data from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params   the parameters for this request. Use {@link Accelerometer.Params#getDefault}
+     *                 as a default
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void addAccelerometerListener(@NonNull Accelerometer.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<Accelerometer> listener);
+
+    /**
+     * Remove an ongoing listener for {@link Accelerometer} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeAccelerometerListener(@NonNull OnCarDataListener<Accelerometer> listener);
+
+    /**
+     * Setup an ongoing listener to receive {@link Gyroscope} data from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params   the parameters for this request. Use {@link Gyroscope.Params#getDefault}
+     *                 as a default
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void addGyroscopeListener(@NonNull Gyroscope.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<Gyroscope> listener);
+
+    /**
+     * Remove an ongoing listener for {@link Gyroscope} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeGyroscopeListener(@NonNull OnCarDataListener<Gyroscope> listener);
+
+    /**
+     * Setup an ongoing listener to receive {@link Compass} data from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params   the parameters for this request. Use {@link Compass.Params#getDefault}
+     *                 as a default
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void addCompassListener(@NonNull Compass.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<Compass> listener);
+
+    /**
+     * Remove an ongoing listener for {@link Compass} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeCompassListener(@NonNull OnCarDataListener<Compass> listener);
+
+    /**
+     * Setup an ongoing listener to receive {@link CarHardwareLocation} data from the car hardware.
+     *
+     * <p>If the listener was added previously then previous params are updated with the new params.
+     *
+     * @param params   the parameters for this request. Use
+     *                 {@link CarHardwareLocation.Params#getDefault}  as a default
+     * @param executor the executor which will be used for invoking the listener
+     * @param listener the listener to use
+     */
+    void addCarHardwareLocationListener(@NonNull CarHardwareLocation.Params params,
+            @NonNull Executor executor,
+            @NonNull OnCarDataListener<CarHardwareLocation> listener);
+
+    /**
+     * Remove an ongoing listener for {@link CarHardwareLocation} information.
+     *
+     * <p>If the listener is not currently added, this call will be ignored.
+     *
+     * @param listener the listener to use
+     */
+    void removeCarHardwareLocationListener(
+            @NonNull OnCarDataListener<CarHardwareLocation> listener);
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Compass.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Compass.java
new file mode 100644
index 0000000..1ed0fd9
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Compass.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific compass available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Compass {
+
+    /** Compass request parameters. */
+    public static final class Params {
+        private final @UpdateRate.Value int mRate;
+
+        /**
+         * Construct compass parameter instance.
+         */
+        public Params(@UpdateRate.Value int rate) {
+            mRate = rate;
+        }
+
+        /** Gets the requested data rate for the compass. */
+        public @UpdateRate.Value int getRate() {
+            return mRate;
+        }
+
+        /** Gets an {@link Compass.Params} instance with default values set. */
+        public static @NonNull Compass.Params getDefault() {
+            return new Params(UpdateRate.DEFAULT);
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<Float[]> mCompass;
+
+    /**
+     * Returns the raw compass data from the car sensor.
+     *
+     * <p>Follows the same format as {@link android.hardware.SensorEvent#values}.
+     */
+    @NonNull
+    public CarValue<Float[]> getCompass() {
+        return requireNonNull(mCompass);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ compass: " + mCompass + " ]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCompass);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Compass)) {
+            return false;
+        }
+        Compass otherCompass = (Compass) other;
+
+        return Objects.equals(mCompass, otherCompass.mCompass);
+    }
+
+    Compass(Builder builder) {
+        mCompass = requireNonNull(builder.mCompass);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private Compass() {
+        mCompass = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+    }
+
+    /** A builder of {@link Compass}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Float[]> mCompass;
+
+        /**
+         * Sets the raw compass data.
+         *
+         * @throws NullPointerException if {@code compass} is {@code null}
+         */
+        @NonNull
+        public Builder setCompass(@NonNull CarValue<Float[]> compass) {
+            mCompass = requireNonNull(compass);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link Compass} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public Compass build() {
+            if (mCompass == null) {
+                mCompass = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+            }
+            return new Compass(this);
+        }
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java
new file mode 100644
index 0000000..86955d8
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarUnit;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information of the energy (fuel and battery) levels from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class EnergyLevel {
+
+    /** Energy level request parameters. */
+    public static final class Params {
+        private final @UpdateRate.Value int mRate;
+
+        private static final EnergyLevel.Params DEFAULT = new Params(UpdateRate.DEFAULT);
+
+        /** Construct {@link EnergyLevel} parameter instance. */
+        public Params(@UpdateRate.Value int rate) {
+            mRate = rate;
+        }
+
+        /** Gets the requested data rate for the energy level. */
+        public @UpdateRate.Value int getRate() {
+            return mRate;
+        }
+
+        /** Gets an {@link EnergyLevel.Params} instance with default values set. */
+        public static @NonNull EnergyLevel.Params getDefault() {
+            return DEFAULT;
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<Float> mBatteryPercent;
+
+    @Keep
+    @NonNull
+    private final CarValue<Float> mFuelPercent;
+
+    @Keep
+    @NonNull
+    private final CarValue<Boolean> mEnergyIsLow;
+
+    @Keep
+    @NonNull
+    private final CarValue<Float> mRangeRemaining;
+
+    @Keep
+    @NonNull
+    private final CarValue<Integer> mDistanceDisplayUnit;
+
+    /** Returns the battery percentage remaining from the car hardware. */
+    @NonNull
+    public CarValue<Float> getBatteryPercent() {
+        return requireNonNull(mBatteryPercent);
+    }
+
+    /** Returns the fuel percentage remaining from the car hardware. */
+    @NonNull
+    public CarValue<Float> getFuelPercent() {
+        return requireNonNull(mFuelPercent);
+    }
+
+    /** Returns if the remaining car energy is low from the car hardware. */
+    @NonNull
+    public CarValue<Boolean> getEnergyIsLow() {
+        return requireNonNull(mEnergyIsLow);
+    }
+
+    /** Returns the range remaining from the car hardware in meters. */
+    @NonNull
+    public CarValue<Float> getRangeRemaining() {
+        return requireNonNull(mRangeRemaining);
+    }
+
+    /**
+     * Returns the distance display unit from the car hardware.
+     *
+     * <p>See {@link CarUnit} for possible distance values.
+     */
+    @NonNull
+    public CarValue<Integer> getDistanceDisplayUnit() {
+        return requireNonNull(mDistanceDisplayUnit);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ battery percent: "
+                + mBatteryPercent
+                + ", fuel percent: "
+                + mFuelPercent
+                + ", energyIsLow: "
+                + mEnergyIsLow
+                + ", range remaining: "
+                + mRangeRemaining
+                + ", distance display unit: "
+                + mDistanceDisplayUnit
+                + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mBatteryPercent, mFuelPercent, mEnergyIsLow, mRangeRemaining,
+                mDistanceDisplayUnit);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof EnergyLevel)) {
+            return false;
+        }
+        EnergyLevel otherEnergyLevel = (EnergyLevel) other;
+
+        return Objects.equals(mBatteryPercent, otherEnergyLevel.mBatteryPercent)
+                && Objects.equals(mFuelPercent, otherEnergyLevel.mFuelPercent)
+                && Objects.equals(mEnergyIsLow, otherEnergyLevel.mEnergyIsLow)
+                && Objects.equals(mRangeRemaining, otherEnergyLevel.mRangeRemaining)
+                && Objects.equals(mDistanceDisplayUnit, otherEnergyLevel.mDistanceDisplayUnit);
+    }
+
+    EnergyLevel(Builder builder) {
+        mBatteryPercent = requireNonNull(builder.mBatteryPercent);
+        mFuelPercent = requireNonNull(builder.mFuelPercent);
+        mEnergyIsLow = requireNonNull(builder.mEnergyIsLow);
+        mRangeRemaining = requireNonNull(builder.mRangeRemaining);
+        mDistanceDisplayUnit = requireNonNull(builder.mDistanceDisplayUnit);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private EnergyLevel() {
+        mBatteryPercent = CarValue.UNIMPLEMENTED_FLOAT;
+        mFuelPercent = CarValue.UNIMPLEMENTED_FLOAT;
+        mEnergyIsLow = CarValue.UNIMPLEMENTED_BOOLEAN;
+        mRangeRemaining = CarValue.UNIMPLEMENTED_FLOAT;
+        mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+    }
+
+    /** A builder of {@link EnergyLevel}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Float> mBatteryPercent;
+        @Nullable
+        CarValue<Float> mFuelPercent;
+
+        @Nullable
+        CarValue<Boolean> mEnergyIsLow;
+
+        @Nullable
+        CarValue<Float> mRangeRemaining;
+
+        @Nullable
+        CarValue<Integer> mDistanceDisplayUnit;
+
+        /** Sets the remaining batter percentage. */
+        @NonNull
+        public Builder setBatteryPercent(@NonNull CarValue<Float> batteryPercent) {
+            mBatteryPercent = requireNonNull(batteryPercent);
+            return this;
+        }
+
+        /**
+         * Sets the remaining fuel percentage.
+         *
+         * @throws NullPointerException if {@code fuelPercent} is {@code null}
+         */
+        @NonNull
+        public Builder setFuelPercent(@NonNull CarValue<Float> fuelPercent) {
+            mFuelPercent = requireNonNull(fuelPercent);
+            return this;
+        }
+
+        /**
+         * Sets if the remaining energy is low.
+         *
+         * @throws NullPointerException if {@code energyIsLow} is {@code null}
+         */
+        @NonNull
+        public Builder setEnergyIsLow(@NonNull CarValue<Boolean> energyIsLow) {
+            mEnergyIsLow = requireNonNull(energyIsLow);
+            return this;
+        }
+
+        /**
+         * Sets the range of the remaining fuel in meters.
+         *
+         * @throws NullPointerException if {@code rangeRemaining} is {@code null}
+         */
+        @NonNull
+        public Builder setRangeRemaining(@NonNull CarValue<Float> rangeRemaining) {
+            mRangeRemaining = requireNonNull(rangeRemaining);
+            return this;
+        }
+
+        /**
+         * Sets the distance display unit.
+         *
+         * <p>Valid values are in {@link CarUnit}.
+         *
+         * @throws NullPointerException if {@code distanceDisplayUnit} is {@code null}
+         */
+        @NonNull
+        public Builder setDistanceDisplayUnit(@NonNull CarValue<Integer> distanceDisplayUnit) {
+            mDistanceDisplayUnit = requireNonNull(distanceDisplayUnit);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link EnergyLevel} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public EnergyLevel build() {
+            if (mBatteryPercent == null) {
+                mBatteryPercent = CarValue.UNIMPLEMENTED_FLOAT;
+            }
+            if (mFuelPercent == null) {
+                mFuelPercent = CarValue.UNIMPLEMENTED_FLOAT;
+            }
+            if (mEnergyIsLow == null) {
+                mEnergyIsLow = CarValue.UNIMPLEMENTED_BOOLEAN;
+            }
+            if (mRangeRemaining == null) {
+                mRangeRemaining = CarValue.UNIMPLEMENTED_FLOAT;
+            }
+            if (mDistanceDisplayUnit == null) {
+                mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+            }
+            return new EnergyLevel(this);
+        }
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java
new file mode 100644
index 0000000..f9590f5
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/** Information about car hardware fuel profile such as fuel types and connector ports. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class EnergyProfile {
+
+    /**
+     * Possible EV Connector types.
+     *
+     * @hide
+     */
+    @IntDef({
+            EVCONNECTOR_TYPE_UNKNOWN,
+            EVCONNECTOR_TYPE_J1772,
+            EVCONNECTOR_TYPE_MENNEKES,
+            EVCONNECTOR_TYPE_CHADEMO,
+            EVCONNECTOR_TYPE_COMBO_1,
+            EVCONNECTOR_TYPE_COMBO_2,
+            EVCONNECTOR_TYPE_TESLA_ROADSTER,
+            EVCONNECTOR_TYPE_TESLA_HPWC,
+            EVCONNECTOR_TYPE_TESLA_SUPERCHARGER,
+            EVCONNECTOR_TYPE_GBT,
+            EVCONNECTOR_TYPE_GBT_DC,
+            EVCONNECTOR_TYPE_SCAME,
+            EVCONNECTOR_TYPE_OTHER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface EvConnectorType {}
+
+    /** Unknown connector type. */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_UNKNOWN = 0;
+
+    /** Connector type SAE J1772 */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_J1772 = 1;
+
+    /** IEC 62196 Type 2 connector */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_MENNEKES = 2;
+
+    /** CHAdeMo fast charger connector */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_CHADEMO = 3;
+
+    /** Combined Charging System Combo 1 */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_COMBO_1 = 4;
+
+    /** Combined Charging System Combo 2 */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_COMBO_2 = 5;
+
+    /** Connector of Tesla Roadster */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6;
+
+    /** High Power Wall Charger of Tesla */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7;
+
+    /** Supercharger of Tesla */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8;
+
+    /** GBT_AC Fast Charging Standard */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_GBT = 9;
+
+    /** GBT_DC Fast Charging Standard */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_GBT_DC = 10;
+
+    /** IEC_TYPE_3_AC connector */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_SCAME = 11;
+
+    /**
+     * Connector type to use when no other types apply.
+     */
+    @EvConnectorType
+    public static final int EVCONNECTOR_TYPE_OTHER = 101;
+
+    /** Energy profile request parameters. */
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public static final class Params {
+        public static @NonNull Params getDefault() {
+            return new Params();
+        }
+    }
+
+    /**
+     * Possible Fual types.
+     *
+     * @hide
+     */
+    @IntDef({
+            FUEL_TYPE_UNKNOWN,
+            FUEL_TYPE_UNLEADED,
+            FUEL_TYPE_LEADED,
+            FUEL_TYPE_DIESEL_1,
+            FUEL_TYPE_DIESEL_2,
+            FUEL_TYPE_BIODIESEL,
+            FUEL_TYPE_E85,
+            FUEL_TYPE_LPG,
+            FUEL_TYPE_CNG,
+            FUEL_TYPE_LNG,
+            FUEL_TYPE_ELECTRIC,
+            FUEL_TYPE_HYDROGEN,
+            FUEL_TYPE_OTHER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface FuelType {}
+
+    /** Unknown fuel type */
+    @FuelType public static final int FUEL_TYPE_UNKNOWN = 0;
+    /** Unleaded gasoline */
+    @FuelType public static final int FUEL_TYPE_UNLEADED = 1;
+    /** Leaded gasoline */
+    @FuelType public static final int FUEL_TYPE_LEADED = 2;
+    /** #1 Grade Diesel */
+    @FuelType public static final int FUEL_TYPE_DIESEL_1 = 3;
+    /** #2 Grade Diesel */
+    @FuelType public static final int FUEL_TYPE_DIESEL_2 = 4;
+    /** Biodiesel */
+    @FuelType public static final int FUEL_TYPE_BIODIESEL = 5;
+    /** 85% ethanol/gasoline blend */
+    @FuelType public static final int FUEL_TYPE_E85 = 6;
+    /** Liquified petroleum gas */
+    @FuelType public static final int FUEL_TYPE_LPG = 7;
+    /** Compressed natural gas */
+    @FuelType public static final int FUEL_TYPE_CNG = 8;
+    /** Liquified natural gas */
+    @FuelType public static final int FUEL_TYPE_LNG = 9;
+    /** Electric */
+    @FuelType public static final int FUEL_TYPE_ELECTRIC = 10;
+    /** Hydrogen fuel cell */
+    @FuelType public static final int FUEL_TYPE_HYDROGEN = 11;
+    /** Fuel type to use when no other types apply. */
+    @FuelType public static final int FUEL_TYPE_OTHER = 12;
+
+    @Keep
+    @NonNull
+    private final CarValue<Integer[]> mEvConnectorTypes;
+
+    @Keep
+    @NonNull
+    private final CarValue<Integer[]> mFuelTypes;
+
+    /**
+     *  Returns an array of the available EV connectors.
+     *
+     *  <p>If a vehicle does not know the EV connector type it will return
+     *  {@link #EVCONNECTOR_TYPE_UNKNOWN} or {@link CarValue#STATUS_UNIMPLEMENTED}. If the value
+     *  is known but not in the current list {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
+     */
+    @NonNull
+    public CarValue<Integer[]> getEvConnectorTypes() {
+        return requireNonNull(mEvConnectorTypes);
+    }
+
+    /**
+     *  Returns an array of the available fuel types.
+     *
+     *  <p>If a vehicle does not know the fuel type it will return {@link #FUEL_TYPE_UNKNOWN} or
+     *  {@link CarValue#STATUS_UNIMPLEMENTED}. If the value is known but not in the current list
+     *  {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
+     */
+    @NonNull
+    public CarValue<Integer[]> getFuelTypes() {
+        return requireNonNull(mFuelTypes);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ evConnectorTypes: " + mEvConnectorTypes + ", fuelTypes: " + mFuelTypes + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mEvConnectorTypes, mFuelTypes);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof EnergyProfile)) {
+            return false;
+        }
+        EnergyProfile otherProfile = (EnergyProfile) other;
+
+        return Objects.equals(mEvConnectorTypes, otherProfile.mEvConnectorTypes)
+                && Objects.equals(mFuelTypes, otherProfile.mFuelTypes);
+    }
+
+    EnergyProfile(Builder builder) {
+        mEvConnectorTypes = requireNonNull(builder.mEvConnectorTypes);
+        mFuelTypes = requireNonNull(builder.mFuelTypes);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private EnergyProfile() {
+        mEvConnectorTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+        mFuelTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+    }
+
+    /** A builder of {@link EnergyProfile}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Integer[]> mEvConnectorTypes;
+        @Nullable
+        CarValue<Integer[]> mFuelTypes;
+
+        /**
+         * Sets the cars EV connector types.
+         *
+         * @throws NullPointerException if {@code evConnectorTypes} is {@code null}
+         */
+        @NonNull
+        public Builder setEvConnectorTypes(@NonNull CarValue<Integer[]> evConnectorTypes) {
+            mEvConnectorTypes = requireNonNull(evConnectorTypes);
+            return this;
+        }
+
+        /**
+         * Sets the cars fuel types.
+         *
+         * @throws NullPointerException if {@code fuelTypes} is {@code null}
+         */
+        @NonNull
+        public Builder setFuelTypes(@NonNull CarValue<Integer[]> fuelTypes) {
+            mFuelTypes = requireNonNull(fuelTypes);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link EnergyProfile} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public EnergyProfile build() {
+            if (mEvConnectorTypes == null) {
+                mEvConnectorTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+            }
+            if (mFuelTypes == null) {
+                mFuelTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+            }
+            return new EnergyProfile(this);
+        }
+
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Gyroscope.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Gyroscope.java
new file mode 100644
index 0000000..38eee39
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Gyroscope.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific gyroscopes available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Gyroscope {
+
+    /** Gyroscope request parameters. */
+    public static final class Params {
+        private final @UpdateRate.Value int mRate;
+
+        /**
+         * Construct gyroscope parameter instance.
+         */
+        public Params(@UpdateRate.Value int rate) {
+            mRate = rate;
+        }
+
+        /** Gets the requested data rate for the gyroscope. */
+        public @UpdateRate.Value int getRate() {
+            return mRate;
+        }
+
+        /** Gets an {@link Gyroscope.Params} instance with default values set. */
+        public static @NonNull Gyroscope.Params getDefault() {
+            return new Params(UpdateRate.DEFAULT);
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<Float[]> mGyroscope;
+
+    /** Returns the raw gyroscope data from the car sensor. */
+    @NonNull
+    public CarValue<Float[]> getGyroscope() {
+        return requireNonNull(mGyroscope);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ gyroscope: " + mGyroscope + " ]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mGyroscope);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Gyroscope)) {
+            return false;
+        }
+        Gyroscope otherGyroscope = (Gyroscope) other;
+
+        return Objects.equals(mGyroscope, otherGyroscope.mGyroscope);
+    }
+
+    Gyroscope(Builder builder) {
+        mGyroscope = requireNonNull(builder.mGyroscope);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private Gyroscope() {
+        mGyroscope = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+    }
+
+    /** A builder of {@link Gyroscope}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Float[]> mGyroscope;
+
+        /**
+         * Sets the raw gyroscope data.
+         *
+         * @throws NullPointerException if {@code gyroscope} is {@code null}
+         */
+        @NonNull
+        public Builder setGyroscope(@NonNull CarValue<Float[]> gyroscope) {
+            mGyroscope = requireNonNull(gyroscope);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link Gyroscope} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public Gyroscope build() {
+            if (mGyroscope == null) {
+                mGyroscope = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+            }
+            return new Gyroscope(this);
+        }
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java
new file mode 100644
index 0000000..fb93d0d
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarUnit;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car mileage. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Mileage {
+
+    /** Mileage request parameters. */
+    public static final class Params {
+        private final @UpdateRate.Value int mRate;
+
+        public Params(@UpdateRate.Value int rate) {
+            mRate = rate;
+        }
+
+        public @UpdateRate.Value int getRate() {
+            return mRate;
+        }
+
+        public static @NonNull Mileage.Params getDefault() {
+            return new Params(UpdateRate.DEFAULT);
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<Float> mOdometer;
+
+    @Keep
+    @NonNull
+    private final CarValue<Integer> mDistanceDisplayUnit;
+
+    /** Returns the value of the odometer from the car hardware in meters. */
+    @NonNull
+    public CarValue<Float> getOdometer() {
+        return requireNonNull(mOdometer);
+    }
+
+    /**
+     * Returns the distance display unit from the car hardware.
+     *
+     * <p>See {@link CarUnit} for possible distance values.
+     */
+    @NonNull
+    public CarValue<Integer> getDistanceDisplayUnit() {
+        return requireNonNull(mDistanceDisplayUnit);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ odometer: "
+                + mOdometer
+                + ", distance display unit: "
+                + mDistanceDisplayUnit
+                + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mOdometer, mDistanceDisplayUnit);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Mileage)) {
+            return false;
+        }
+        Mileage otherMileage = (Mileage) other;
+
+        return Objects.equals(mOdometer, otherMileage.mOdometer)
+                && Objects.equals(mDistanceDisplayUnit, otherMileage.mDistanceDisplayUnit);
+    }
+
+    Mileage(Builder builder) {
+        mOdometer = requireNonNull(builder.mOdometer);
+        mDistanceDisplayUnit = requireNonNull(builder.mDistanceDisplayUnit);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private Mileage() {
+        mOdometer = CarValue.UNIMPLEMENTED_FLOAT;
+        mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+    }
+
+    /** A builder of {@link Mileage}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Float> mOdometer;
+        @Nullable
+        CarValue<Integer> mDistanceDisplayUnit;
+
+        /**
+         * Sets the odometer value in meters.
+         *
+         * @throws NullPointerException if {@code odometer} is {@code null}
+         */
+        @NonNull
+        public Builder setOdometer(@NonNull CarValue<Float> odometer) {
+            mOdometer = requireNonNull(odometer);
+            return this;
+        }
+
+        /**
+         * Sets the mileage display unit.
+         *
+         * <p>Valid values are in {@link CarUnit}.
+         *
+         * @throws NullPointerException if {@code mileageDisplayUnit} is {@code null}
+         */
+        @NonNull
+        public Builder setDistanceDisplayUnit(@NonNull CarValue<Integer> mileageDisplayUnit) {
+            mDistanceDisplayUnit = requireNonNull(mileageDisplayUnit);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link Mileage} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public Mileage build() {
+            if (mOdometer == null) {
+                mOdometer = CarValue.UNIMPLEMENTED_FLOAT;
+            }
+            if (mDistanceDisplayUnit == null) {
+                mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+            }
+            return new Mileage(this);
+        }
+
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Model.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Model.java
new file mode 100644
index 0000000..5af402b
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Model.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+
+import java.util.Objects;
+
+/**
+ * Information about the androidx.car.app.hardware model such as name, year and manufacturer.
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Model {
+
+    /** Model request parameters. */
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public static final class Params {
+        public static @NonNull Params getDefault() {
+            return new Params();
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<String> mName;
+
+    @Keep
+    @NonNull
+    private final CarValue<Integer> mYear;
+
+    @Keep
+    @NonNull
+    private final CarValue<String> mManufacturer;
+
+    /** Returns the car model name. */
+    @NonNull
+    public CarValue<String> getName() {
+        return requireNonNull(mName);
+    }
+
+    /** Returns the car model year. */
+    @NonNull
+    public CarValue<Integer> getYear() {
+        return requireNonNull(mYear);
+    }
+
+    /** Returns the car manufacturer. */
+    @NonNull
+    public CarValue<String> getManufacturer() {
+        return requireNonNull(mManufacturer);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ name: " + mName + ", year: " + mYear + ", manufacturer: " + mManufacturer + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mYear, mManufacturer);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Model)) {
+            return false;
+        }
+        Model otherModel = (Model) other;
+
+        return Objects.equals(mName, otherModel.mName)
+                && Objects.equals(mYear, otherModel.mYear)
+                && Objects.equals(mManufacturer, otherModel.mManufacturer);
+    }
+
+    Model(Builder builder) {
+        mName = requireNonNull(builder.mName);
+        mManufacturer = requireNonNull(builder.mManufacturer);
+        mYear = requireNonNull(builder.mYear);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private Model() {
+        mName = CarValue.UNIMPLEMENTED_STRING;
+        mManufacturer = CarValue.UNIMPLEMENTED_STRING;
+        mYear = CarValue.UNIMPLEMENTED_INTEGER;
+    }
+
+    /** A builder of {@link Model}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<String> mName;
+        @Nullable
+        CarValue<Integer> mYear;
+        @Nullable
+        CarValue<String> mManufacturer;
+
+        /**
+         * Sets the car model name.
+         *
+         * @throws NullPointerException if {@code name} is {@code null}
+         */
+        @NonNull
+        public Builder setName(@NonNull CarValue<String> name) {
+            mName = requireNonNull(name);
+            return this;
+        }
+
+        /**
+         * Sets the car model year.
+         *
+         * @throws NullPointerException if {@code year} is {@code null}
+         */
+        @NonNull
+        public Builder setYear(@NonNull CarValue<Integer> year) {
+            mYear = requireNonNull(year);
+            return this;
+        }
+
+        /**
+         * Sets the car manufacturer.
+         *
+         * @throws NullPointerException if {@code manufacturer} is {@code null}
+         */
+        @NonNull
+        public Builder setManufacturer(@NonNull CarValue<String> manufacturer) {
+            mManufacturer = requireNonNull(manufacturer);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link Model} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public Model build() {
+            if (mName == null) {
+                mName = CarValue.UNIMPLEMENTED_STRING;
+            }
+            if (mYear == null) {
+                mYear = CarValue.UNIMPLEMENTED_INTEGER;
+            }
+            if (mManufacturer == null) {
+                mManufacturer = CarValue.UNIMPLEMENTED_STRING;
+            }
+            return new Model(this);
+        }
+
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java
new file mode 100644
index 0000000..bfbc856
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarUnit;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/**
+ * Information about the current car speed.
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Speed {
+
+    /**
+     * Parameters for speed requests.
+     */
+    public static final class Params {
+        private final @UpdateRate.Value int mRate;
+
+        public Params(@UpdateRate.Value int rate) {
+            mRate = rate;
+        }
+
+        public @UpdateRate.Value int getRate() {
+            return mRate;
+        }
+
+        public static @NonNull Speed.Params getDefault() {
+            return new Params(UpdateRate.DEFAULT);
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<Float> mRawSpeed;
+
+    @Keep
+    @NonNull
+    private final CarValue<Float> mDisplaySpeed;
+
+    @Keep
+    @NonNull
+    private final CarValue<Integer> mSpeedDisplayUnit;
+
+    /** Returns the raw speed of the car in meters/second. */
+    @NonNull
+    public CarValue<Float> getRawSpeed() {
+        return requireNonNull(mRawSpeed);
+    }
+
+    /** Returns the display speed of the car in meters/second. */
+    @NonNull
+    public CarValue<Float> getDisplaySpeed() {
+        return requireNonNull(mDisplaySpeed);
+    }
+
+    /**
+     * Returns the units used to display speed from the car settings.
+     *
+     * <p>See {@link CarUnit} for valid speed units.
+     */
+    @NonNull
+    public CarValue<Integer> getSpeedDisplayUnit() {
+        return requireNonNull(mSpeedDisplayUnit);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ raw speed: "
+                + mRawSpeed
+                + ", display speed: "
+                + mDisplaySpeed
+                + ", speed display unit: "
+                + mSpeedDisplayUnit
+                + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRawSpeed, mDisplaySpeed, mSpeedDisplayUnit);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Speed)) {
+            return false;
+        }
+        Speed otherSpeed = (Speed) other;
+
+        return Objects.equals(mRawSpeed, otherSpeed.mRawSpeed)
+                && Objects.equals(mDisplaySpeed, otherSpeed.mDisplaySpeed)
+                && Objects.equals(mSpeedDisplayUnit, otherSpeed.mSpeedDisplayUnit);
+    }
+
+    Speed(Builder builder) {
+        mRawSpeed = requireNonNull(builder.mRawSpeed);
+        mDisplaySpeed = requireNonNull(builder.mDisplaySpeed);
+        mSpeedDisplayUnit = requireNonNull(builder.mSpeedDisplayUnit);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private Speed() {
+        mRawSpeed = CarValue.UNIMPLEMENTED_FLOAT;
+        mDisplaySpeed = CarValue.UNIMPLEMENTED_FLOAT;
+        mSpeedDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+    }
+
+    /** A builder of {@link Speed}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Float> mRawSpeed;
+        @Nullable
+        CarValue<Float> mDisplaySpeed;
+
+        @Nullable
+        CarValue<Integer> mSpeedDisplayUnit;
+
+        /**
+         * Sets the raw speed.
+         *
+         * @throws NullPointerException if {@code rawSpeed} is {@code null}
+         */
+        @NonNull
+        public Builder setRawSpeed(@NonNull CarValue<Float> rawSpeed) {
+            mRawSpeed = requireNonNull(rawSpeed);
+            return this;
+        }
+
+        /**
+         * Sets the display speed. *
+         *
+         * @throws NullPointerException if {@code displaySpeed} is {@code null}
+         */
+        @NonNull
+        public Builder setDisplaySpeed(@NonNull CarValue<Float> displaySpeed) {
+            mDisplaySpeed = requireNonNull(displaySpeed);
+            return this;
+        }
+
+        /**
+         * Sets the units used to display speed from the car hardware settings.
+         *
+         * <p>See {@link CarUnit} for valid speed units.
+         *
+         * @throws NullPointerException if {@code speedDisplayUnit} is {@code null}
+         */
+        @NonNull
+        public Builder setSpeedDisplayUnit(@NonNull CarValue<Integer> speedDisplayUnit) {
+            mSpeedDisplayUnit = requireNonNull(speedDisplayUnit);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link Speed} defined by this builder.
+         *
+         * <p>Any fields which have not been set are added with {@code null} value and
+         * {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public Speed build() {
+            if (mRawSpeed == null) {
+                mRawSpeed = CarValue.UNIMPLEMENTED_FLOAT;
+            }
+            if (mDisplaySpeed == null) {
+                mDisplaySpeed = CarValue.UNIMPLEMENTED_FLOAT;
+            }
+            if (mSpeedDisplayUnit == null) {
+                mSpeedDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+            }
+            return new Speed(this);
+        }
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Toll.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Toll.java
new file mode 100644
index 0000000..17de543
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Toll.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2021 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.car.app.hardware.info;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Information about toll card capabilities in a car.
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Toll {
+
+    /**
+     * Possible toll card states.
+     *
+     * @hide
+     */
+    @IntDef({
+            TOLLCARD_STATE_UNKNOWN,
+            TOLLCARD_STATE_VALID,
+            TOLLCARD_STATE_INVALID,
+            TOLLCARD_STATE_NOT_INSERTED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @RestrictTo(LIBRARY)
+    public @interface State {
+    }
+
+    /**
+     * Toll card state is unknown.
+     */
+    @State
+    public static final int TOLLCARD_STATE_UNKNOWN = 0;
+
+    /**
+     * Toll card state is valid.
+     */
+    @State
+    public static final int TOLLCARD_STATE_VALID = 1;
+
+    /**
+     * Toll card state invalid.
+     *
+     * <p>On some vehicles this may be that the toll card is inserted but not valid while other
+     * vehicles might not have a toll card inserted.
+     */
+    @State
+    public static final int TOLLCARD_STATE_INVALID = 2;
+
+    /**
+     * Toll card state is not inserted.
+     *
+     * <p>Will be returned if the car hardware is able to detect that the card is not inserted.
+     */
+    @State
+    public static final int TOLLCARD_STATE_NOT_INSERTED = 3;
+
+    /** Toll card request parameters. */
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    public static final class Params {
+        public static @NonNull Params getDefault() {
+            return new Params();
+        }
+    }
+
+    @Keep
+    @NonNull
+    private final CarValue<Integer> mCardState;
+
+    /** Returns the toll card state if available. */
+    @NonNull
+    public CarValue<Integer> getCardState() {
+        return requireNonNull(mCardState);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "[ card state: " + mCardState + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCardState);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Toll)) {
+            return false;
+        }
+        Toll otherToll = (Toll) other;
+
+        return Objects.equals(mCardState, otherToll.mCardState);
+    }
+
+    Toll(Builder builder) {
+        mCardState = requireNonNull(builder.mCardState);
+    }
+
+    /** Constructs an empty instance, used by serialization code. */
+    private Toll() {
+        mCardState = CarValue.UNIMPLEMENTED_INTEGER;
+    }
+
+    /** A builder of {@link Toll}. */
+    public static final class Builder {
+        @Nullable
+        CarValue<Integer> mCardState;
+
+        /**
+         * Sets the toll card state.
+         *
+         * @throws NullPointerException if {@code cardState} is {@code null}
+         */
+        @NonNull
+        public Builder setCardState(@NonNull CarValue<Integer> cardState) {
+            mCardState = requireNonNull(cardState);
+            return this;
+        }
+
+        /**
+         * Constructs the {@link Toll} defined by this builder.
+         *
+         *  <p>Any fields which have not been set are added with {@code null} value and
+         *  {@link CarValue#STATUS_UNIMPLEMENTED}.
+         */
+        @NonNull
+        public Toll build() {
+            if (mCardState == null) {
+                mCardState = CarValue.UNIMPLEMENTED_INTEGER;
+            }
+            return new Toll(this);
+        }
+
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
index 1003326..3e42b54f 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
@@ -51,9 +51,9 @@
  * <p>Similar to Android devices, car screens cover a wide range of sizes and densities. To
  * ensure that icons and images render well across all car screens, use vector assets whenever
  * possible to avoid scaling issues. If your app relies on bitmaps or other non-vector
- * assets, you should ensure that you have resources that address multiple size and  pixel density
- * buckets using configuration qualifiers in your resource folders (e.g. "large", "xlarge", "mdpi",
- * "hdpi", etc). See {@link androidx.car.app.CarContext} for more details.
+ * assets, you should ensure that you have resources that address multiple pixel density
+ * buckets using configuration qualifiers in your resource folders (e.g. "mdpi", "hdpi", etc).
+ * See {@link androidx.car.app.CarContext} for more details.
  *
  * <h4>Themed Drawables</h4>
  *
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
index 57be50d..fb829cb 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
@@ -162,7 +162,7 @@
          *
          * Images in the cue of the {@link Step} object, set with {@link Step.Builder#setCue}, can
          * contain image spans. To minimize scaling artifacts across a wide range of car screens,
-         * apps should provide images targeting a 250 x 83 dp bounding box. If necessary, those
+         * apps should provide images targeting a 216 x 72 dp bounding box. If necessary, those
          * images in the spans will be scaled down to fit the bounding box while preserving their
          * aspect ratios.
          *
@@ -189,7 +189,7 @@
          *
          * Images in the cue of the {@link Step} object, set with {@link Step.Builder#setCue}, can
          * contain image spans. To minimize scaling artifacts across a wide range of car screens,
-         * apps should provide images targeting a 250 x 83 dp bounding box. If necessary, those
+         * apps should provide images targeting a 216 x 72 dp bounding box. If necessary, those
          * images in the spans will be scaled down to fit the bounding box while preserving their
          * aspect ratios.
          *
diff --git a/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
index 881a154..7accc41 100644
--- a/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
+++ b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
@@ -31,7 +31,7 @@
     /**
      * API level 3.
      *
-     * <p>Includes a vehicle manager for access to sensors and other vehicle properties.
+     * <p>Includes a car hardware manager for access to sensors and other vehicle properties.
      */
     @CarAppApiLevel
     public static final int LEVEL_3 = 3;
diff --git a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
index 502ea8bc..e37c8c7 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
@@ -42,6 +42,7 @@
 
 import androidx.activity.OnBackPressedCallback;
 import androidx.annotation.Nullable;
+import androidx.car.app.hardware.CarHardwareManager;
 import androidx.car.app.navigation.NavigationManager;
 import androidx.car.app.testing.TestLifecycleOwner;
 import androidx.lifecycle.Lifecycle.Event;
@@ -69,6 +70,7 @@
     private static final String APP_SERVICE = "app";
     private static final String NAVIGATION_SERVICE = "navigation";
     private static final String SCREEN_SERVICE = "screen";
+    private static final String HARDWARE_SERVICE = "hardware";
 
     @Mock
     private ICarHost mMockCarHost;
@@ -144,6 +146,12 @@
     }
 
     @Test
+    public void getCarService_hardwareManager() {
+        assertThrows(IllegalStateException.class, () ->
+                mCarContext.getCarService(CarContext.HARDWARE_SERVICE));
+    }
+
+    @Test
     public void getCarService_unknown_throws() {
         assertThrows(IllegalArgumentException.class, () -> mCarContext.getCarService("foo"));
     }
@@ -173,6 +181,12 @@
     }
 
     @Test
+    public void getCarServiceName_hardwareManager_throws() {
+        assertThat(mCarContext.getCarServiceName(CarHardwareManager.class)).isEqualTo(
+                HARDWARE_SERVICE);
+    }
+
+    @Test
     public void getCarServiceName_unexpectedClass_throws() {
         assertThrows(
                 IllegalArgumentException.class,
diff --git a/car/app/app/src/test/java/androidx/car/app/hardware/common/CarValueTest.java b/car/app/app/src/test/java/androidx/car/app/hardware/common/CarValueTest.java
new file mode 100644
index 0000000..7ac9981
--- /dev/null
+++ b/car/app/app/src/test/java/androidx/car/app/hardware/common/CarValueTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 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.car.app.hardware.common;
+
+/** Tests for {@link VehicleException}. */
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class CarValueTest {
+
+    @Test
+    public void createInstance() {
+        String value = "VALUE";
+        long timeStampMillis = 10;
+        int status = CarValue.STATUS_UNIMPLEMENTED;
+        CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+
+        assertThat(value).isEqualTo(carValue.getValue());
+        assertThat(timeStampMillis).isEqualTo(carValue.getTimestampMillis());
+        assertThat(status).isEqualTo(carValue.getStatus());
+    }
+
+    @Test
+    public void equals() {
+        String value = "VALUE";
+        long timeStampMillis = 10;
+        int status = CarValue.STATUS_UNIMPLEMENTED;
+        CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+        assertThat(new CarValue<>(value, timeStampMillis, status)).isEqualTo(carValue);
+    }
+
+    @Test
+    public void notEquals_differentValue() {
+        String value = "VALUE";
+        long timeStampMillis = 10;
+        int status = CarValue.STATUS_UNIMPLEMENTED;
+        CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+        assertThat(new CarValue<>("other", timeStampMillis, status))
+                .isNotEqualTo(carValue);
+    }
+
+    @Test
+    public void notEquals_differentTimestamp() {
+        String value = "VALUE";
+        long timeStampMillis = 10;
+        int status = CarValue.STATUS_UNIMPLEMENTED;
+        CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+        assertThat(new CarValue<>(value, 20, status)).isNotEqualTo(carValue);
+    }
+
+    @Test
+    public void notEquals_differentStatus() {
+        String value = "VALUE";
+        long timeStampMillis = 10;
+        int status = CarValue.STATUS_UNIMPLEMENTED;
+        CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+        assertThat(new CarValue<>(value, timeStampMillis,
+                CarValue.STATUS_UNAVAILABLE)).isNotEqualTo(carValue);
+    }
+
+}
diff --git a/compose/androidview/androidview/lint-baseline.xml b/compose/androidview/androidview/lint-baseline.xml
index 42a176b..f268ca37 100644
--- a/compose/androidview/androidview/lint-baseline.xml
+++ b/compose/androidview/androidview/lint-baseline.xml
@@ -1,4 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
 
+    <issue
+        id="BanUncheckedReflection"
+        message="Calling Method.invoke without an SDK check"
+        errorLine1="        val lp = prev ?: genDefaultLayoutParams.invoke(parent) as? ViewGroup.LayoutParams"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/compose/androidview/adapters/LayoutBuilder.kt"
+            line="96"
+            column="26"/>
+    </issue>
+
 </issues>
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index 08e3fe9..e17879b 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -159,7 +159,7 @@
             project: Project,
             configuration: CompilerConfiguration
         ) {
-            val KOTLIN_VERSION_EXPECTATION = "1.5.0"
+            val KOTLIN_VERSION_EXPECTATION = "1.5.10"
             KotlinCompilerVersion.getVersion()?.let { version ->
                 val suppressKotlinVersionCheck = configuration.get(
                     ComposeConfiguration.SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableSymbolRemapper.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableSymbolRemapper.kt
index 722fd6c..6974105 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableSymbolRemapper.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableSymbolRemapper.kt
@@ -16,22 +16,20 @@
 
 package androidx.compose.compiler.plugins.kotlin.lower
 
-import org.jetbrains.kotlin.ir.IrElement
-import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
-import org.jetbrains.kotlin.ir.declarations.IrConstructor
-import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
-import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
-import org.jetbrains.kotlin.ir.declarations.IrValueParameter
+import androidx.compose.compiler.plugins.kotlin.hasComposableAnnotation
+import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.ParameterDescriptor
+import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
+import org.jetbrains.kotlin.ir.descriptors.IrBasedDeclarationDescriptor
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
 import org.jetbrains.kotlin.ir.util.DescriptorsRemapper
-import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
-import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
+import org.jetbrains.kotlin.types.KotlinType
 
 /**
- * This symbol remapper is aware of possible wrapped descriptor ownership change to align
+ * This symbol remapper is aware of possible descriptor signature change to align
  * function signature and descriptor signature in cases of composable value parameters.
- * As wrapped descriptors are bound to IR functions inside, we need to create a new one to change
- * the function this descriptor represents as well.
+ * It removes descriptors whenever the signature changes, forcing it to be generated from IR.
  *
  * E.g. when function has a signature of:
  * ```
@@ -43,37 +41,51 @@
  * ```
  * Same applies for receiver and return types.
  *
- * After remapping them, the newly created descriptors are bound back using
- * [WrappedComposableDescriptorPatcher] right after IR counterparts are created
- * (see usages in [ComposerTypeRemapper])
- *
  * This conversion is only required with decoys, but can be applied to the JVM as well for
  * consistency.
  */
 class ComposableSymbolRemapper : DeepCopySymbolRemapper(
     object : DescriptorsRemapper {
+        override fun remapDeclaredConstructor(
+            descriptor: ClassConstructorDescriptor
+        ): ClassConstructorDescriptor? =
+            descriptor.takeUnless { it.isTransformed() }
+
+        override fun remapDeclaredSimpleFunction(
+            descriptor: FunctionDescriptor
+        ): FunctionDescriptor? =
+            descriptor.takeUnless { it.isTransformed() }
+
+        override fun remapDeclaredValueParameter(
+            descriptor: ParameterDescriptor
+        ): ParameterDescriptor? =
+            descriptor.takeUnless { it.isTransformed() }
+
+        override fun remapDeclaredTypeParameter(
+            descriptor: TypeParameterDescriptor
+        ): TypeParameterDescriptor? =
+            descriptor.takeUnless { it.isTransformed() }
+
+        private fun ClassConstructorDescriptor.isTransformed(): Boolean =
+            this is IrBasedDeclarationDescriptor<*> ||
+                valueParameters.any { it.type.containsComposable() }
+
+        private fun FunctionDescriptor.isTransformed(): Boolean =
+            this is IrBasedDeclarationDescriptor<*> ||
+                valueParameters.any { it.type.containsComposable() } ||
+                returnType?.containsComposable() == true
+
+        private fun ParameterDescriptor.isTransformed(): Boolean =
+            this is IrBasedDeclarationDescriptor<*> ||
+                type.containsComposable() ||
+                containingDeclaration.let { it is FunctionDescriptor && it.isTransformed() }
+
+        private fun TypeParameterDescriptor.isTransformed(): Boolean =
+            this is IrBasedDeclarationDescriptor<*> ||
+                containingDeclaration.let { it is FunctionDescriptor && it.isTransformed() }
+
+        private fun KotlinType.containsComposable() =
+            hasComposableAnnotation() ||
+                arguments.any { it.type.hasComposableAnnotation() }
     }
 )
-
-@OptIn(ObsoleteDescriptorBasedAPI::class)
-object WrappedComposableDescriptorPatcher : IrElementVisitorVoid {
-    override fun visitElement(element: IrElement) {
-        element.acceptChildrenVoid(this)
-    }
-
-    override fun visitConstructor(declaration: IrConstructor) {
-        super.visitConstructor(declaration)
-    }
-
-    override fun visitSimpleFunction(declaration: IrSimpleFunction) {
-        super.visitSimpleFunction(declaration)
-    }
-
-    override fun visitValueParameter(declaration: IrValueParameter) {
-        super.visitValueParameter(declaration)
-    }
-
-    override fun visitTypeParameter(declaration: IrTypeParameter) {
-        super.visitTypeParameter(declaration)
-    }
-}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
index b5c9461..b465f4a 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
@@ -91,8 +91,6 @@
 
     override fun visitConstructor(declaration: IrConstructor): IrConstructor {
         return super.visitConstructor(declaration).also {
-            WrappedComposableDescriptorPatcher.visitConstructor(it)
-
             it.copyMetadataFrom(declaration)
         }
     }
@@ -105,8 +103,6 @@
             symbolRemapper.visitSimpleFunction(declaration)
         }
         return super.visitSimpleFunction(declaration).also {
-            WrappedComposableDescriptorPatcher.visitSimpleFunction(it)
-
             it.correspondingPropertySymbol = declaration.correspondingPropertySymbol
             it.copyMetadataFrom(declaration)
         }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
index bff6adf..72b5ecf 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
@@ -600,12 +600,11 @@
         return isInvoke() && dispatchReceiver?.type?.hasComposableAnnotation() == true
     }
 
-    @OptIn(ObsoleteDescriptorBasedAPI::class)
     private fun IrFunction.isNonComposableInlinedLambda(): Boolean {
         for (element in inlinedFunctions) {
             if (element.argument.function != this)
                 continue
-            if (!element.parameter.descriptor.type.hasComposableAnnotation())
+            if (!element.parameter.type.hasComposableAnnotation())
                 return true
         }
         return false
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
index cd726fb..23be234 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
@@ -21,9 +21,6 @@
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
 import org.jetbrains.kotlin.backend.common.ir.isTopLevel
 import org.jetbrains.kotlin.backend.common.ir.remapTypeParameters
-import org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializer
-import org.jetbrains.kotlin.backend.common.serialization.KotlinIrLinker
-import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind.FUNCTION_SYMBOL
 import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
 import org.jetbrains.kotlin.descriptors.ModuleDescriptor
 import org.jetbrains.kotlin.ir.IrElement
@@ -38,6 +35,7 @@
 import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
 import org.jetbrains.kotlin.ir.interpreter.toIrConst
 import org.jetbrains.kotlin.ir.linkage.IrDeserializer
+import org.jetbrains.kotlin.ir.linkage.IrDeserializer.TopLevelSymbolKind.FUNCTION_SYMBOL
 import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
 import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
 import org.jetbrains.kotlin.ir.symbols.IrSymbol
@@ -168,24 +166,10 @@
         }
     }
 
-    // todo(KT-44100): functions generated by this plugin are not referenceable from other modules
     private fun IrDeserializer.getDeclaration(
         moduleDescriptor: ModuleDescriptor,
         idSignature: IdSignature
-    ): IrSymbol? {
-        val moduleDeserializerField =
-            KotlinIrLinker::class.java.getDeclaredField("deserializersForModules")
-        moduleDeserializerField.isAccessible = true
-
-        @Suppress("UNCHECKED_CAST")
-        val moduleMap = moduleDeserializerField.get(this)
-            as Map<ModuleDescriptor, IrModuleDeserializer>
-        val moduleDeserializer = moduleMap[moduleDescriptor] ?: return null
-
-        val symbol = moduleDeserializer.deserializeIrSymbol(idSignature, FUNCTION_SYMBOL)
-        moduleDeserializer.deserializeReachableDeclarations()
-        return symbol
-    }
+    ): IrSymbol = resolveBySignatureInModule(idSignature, FUNCTION_SYMBOL, moduleDescriptor.name)
 }
 
 @OptIn(ObsoleteDescriptorBasedAPI::class)
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
index dc84d26..2591678 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
@@ -53,12 +53,12 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import androidx.compose.ui.window.DialogProperties
 import androidx.compose.ui.window.Notifier
 import androidx.compose.ui.window.Popup
-import androidx.compose.ui.window.Tray
 import androidx.compose.ui.window.WindowDraggableArea
+import androidx.compose.ui.window.v1.Dialog
+import androidx.compose.ui.window.v1.DialogProperties
+import androidx.compose.ui.window.v1.Tray
 import java.awt.Toolkit
 import java.awt.event.ActionEvent
 import java.awt.event.ActionListener
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt
index 0ace057..fd8dd5c 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt
@@ -21,8 +21,8 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.window.Menu
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.Menu
+import androidx.compose.ui.window.v1.MenuBar
 import javax.swing.SwingUtilities
 
 fun main() = SwingUtilities.invokeLater {
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt
index e43c1d2..54c7476 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt
@@ -17,9 +17,9 @@
 
 import androidx.compose.desktop.AppManager
 import androidx.compose.ui.input.key.Key
-import androidx.compose.ui.window.MenuItem
-import androidx.compose.ui.window.KeyStroke
 import androidx.compose.ui.window.Notifier
+import androidx.compose.ui.window.v1.KeyStroke
+import androidx.compose.ui.window.v1.MenuItem
 
 object MenuItems {
     val Exit = MenuItem(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt
index 637e42cc..f3b8f16 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt
@@ -16,10 +16,13 @@
 
 package androidx.compose.foundation.demos.text
 
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
@@ -34,6 +37,7 @@
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.intl.LocaleList
 import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.dp
 
 @Composable
 fun TextSelectionDemo() {
@@ -54,6 +58,20 @@
             TagLine(tag = "enable and disable selection")
             TextDemoSelectionEnableAndDisable()
         }
+        item {
+            TagLine(tag = "fix crashing of longpress in the blank area")
+            SelectionContainer {
+                Text(
+                    text = "Hello World\nHello",
+                    modifier = Modifier.fillMaxWidth()
+                        .border(BorderStroke(1.dp, color = Color.Black))
+                        .height(80.dp)
+                )
+            }
+        }
+        item {
+            TagLine(tag = "")
+        }
     }
 }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
index ed52e33..cd74198 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
@@ -15,6 +15,7 @@
  */
 package androidx.compose.foundation
 
+import android.os.Build
 import android.os.Handler
 import android.os.Looper
 import androidx.annotation.RequiresApi
@@ -24,16 +25,23 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.assertPixels
+import androidx.compose.testutils.assertShape
 import androidx.compose.testutils.runBlockingWithManualClock
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -822,4 +830,73 @@
             )
         }
     }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun horizontalScroller_doesNotClipVerticalOverdraw() {
+        rule.setContent {
+            Box(Modifier.size(60.dp).testTag("container").background(Color.Gray)) {
+                Row(
+                    Modifier
+                        .padding(20.dp)
+                        .fillMaxSize()
+                        .horizontalScroll(rememberScrollState(20))
+                ) {
+                    repeat(4) {
+                        Box(Modifier.size(20.dp).drawOutsideOfBounds())
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("container")
+            .captureToImage()
+            .assertShape(
+                density = rule.density,
+                shape = RectangleShape,
+                shapeColor = Color.Red,
+                backgroundColor = Color.Gray,
+                horizontalPadding = 20.dp,
+                verticalPadding = 0.dp
+            )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun verticalScroller_doesNotClipHorizontalOverdraw() {
+        rule.setContent {
+            Box(Modifier.size(60.dp).testTag("container").background(Color.Gray)) {
+                Column(
+                    Modifier
+                        .padding(20.dp)
+                        .fillMaxSize()
+                        .verticalScroll(rememberScrollState(20))
+                ) {
+                    repeat(4) {
+                        Box(Modifier.size(20.dp).drawOutsideOfBounds())
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("container")
+            .captureToImage()
+            .assertShape(
+                density = rule.density,
+                shape = RectangleShape,
+                shapeColor = Color.Red,
+                backgroundColor = Color.Gray,
+                horizontalPadding = 0.dp,
+                verticalPadding = 20.dp
+            )
+    }
+
+    private fun Modifier.drawOutsideOfBounds() = drawBehind {
+        val inflate = 20.dp.roundToPx().toFloat()
+        drawRect(
+            Color.Red,
+            Offset(-inflate, -inflate),
+            Size(size.width + inflate * 2, size.height + inflate * 2)
+        )
+    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
index c9037c3..dcbbe84 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
@@ -29,6 +29,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.requiredSizeIn
@@ -44,12 +45,15 @@
 import androidx.compose.testutils.WithTouchSlop
 import androidx.compose.testutils.assertIsEqualTo
 import androidx.compose.testutils.assertPixels
+import androidx.compose.testutils.assertShape
 import androidx.compose.testutils.runBlockingWithManualClock
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.ExperimentalTestApi
@@ -1440,6 +1444,95 @@
             .assertTopPositionInRootIsEqualTo(containerSize - itemSizeDp)
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun lazyColumnDoesNotClipHorizontalOverdraw() {
+        rule.setContent {
+            Box(Modifier.size(60.dp).testTag("container").background(Color.Gray)) {
+                LazyColumn(
+                    Modifier
+                        .padding(20.dp)
+                        .fillMaxSize(),
+                    rememberLazyListState(1)
+                ) {
+                    items(4) {
+                        Box(Modifier.size(20.dp).drawOutsideOfBounds())
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("container")
+            .captureToImage()
+            .assertShape(
+                density = rule.density,
+                shape = RectangleShape,
+                shapeColor = Color.Red,
+                backgroundColor = Color.Gray,
+                horizontalPadding = 0.dp,
+                verticalPadding = 20.dp
+            )
+    }
+
+    @Test
+    fun initialScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+        lateinit var state: LazyListState
+        var itemsCount by mutableStateOf(0)
+        rule.setContent {
+            state = rememberLazyListState(2, 10)
+            LazyColumn(Modifier.fillMaxSize(), state) {
+                items(itemsCount) {
+                    Box(Modifier.size(20.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            itemsCount = 100
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+        }
+    }
+
+    @Test
+    fun restoredScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+        lateinit var state: LazyListState
+        var itemsCount = 100
+        val recomposeCounter = mutableStateOf(0)
+        val tester = StateRestorationTester(rule)
+        tester.setContent {
+            state = rememberLazyListState()
+            LazyColumn(Modifier.fillMaxSize(), state) {
+                recomposeCounter.value
+                items(itemsCount) {
+                    Box(Modifier.size(20.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollToItem(2, 10)
+            }
+            itemsCount = 0
+        }
+
+        tester.emulateSavedInstanceStateRestore()
+
+        rule.runOnIdle {
+            itemsCount = 100
+            recomposeCounter.value = 1
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+        }
+    }
+
     private fun SemanticsNodeInteraction.assertTopPositionIsAlmost(expected: Dp) {
         getUnclippedBoundsInRoot().top.assertIsEqualTo(expected, tolerance = 1.dp)
     }
@@ -1486,3 +1579,12 @@
             )
         }
     }
+
+internal fun Modifier.drawOutsideOfBounds() = drawBehind {
+    val inflate = 20.dp.roundToPx().toFloat()
+    drawRect(
+        Color.Red,
+        Offset(-inflate, -inflate),
+        Size(size.width + inflate * 2, size.height + inflate * 2)
+    )
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
index ecd3cba..9ad18ab 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.lazy
 
+import android.os.Build
 import androidx.compose.animation.core.snap
 import androidx.compose.foundation.AutoTestFrameClock
 import androidx.compose.foundation.background
@@ -24,6 +25,8 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.requiredSizeIn
@@ -35,11 +38,13 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.testutils.assertIsEqualTo
+import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.SemanticsNodeInteraction
@@ -49,6 +54,7 @@
 import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.getUnclippedBoundsInRoot
@@ -63,6 +69,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
@@ -1132,6 +1139,95 @@
             .assertLeftPositionInRootIsEqualTo(containerSize - itemSizeDp)
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun lazyRowDoesNotClipHorizontalOverdraw() {
+        rule.setContent {
+            Box(Modifier.size(60.dp).testTag("container").background(Color.Gray)) {
+                LazyRow(
+                    Modifier
+                        .padding(20.dp)
+                        .fillMaxSize(),
+                    rememberLazyListState(1)
+                ) {
+                    items(4) {
+                        Box(Modifier.size(20.dp).drawOutsideOfBounds())
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("container")
+            .captureToImage()
+            .assertShape(
+                density = rule.density,
+                shape = RectangleShape,
+                shapeColor = Color.Red,
+                backgroundColor = Color.Gray,
+                horizontalPadding = 20.dp,
+                verticalPadding = 0.dp
+            )
+    }
+
+    @Test
+    fun initialScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+        lateinit var state: LazyListState
+        var itemsCount by mutableStateOf(0)
+        rule.setContent {
+            state = rememberLazyListState(2, 10)
+            LazyRow(Modifier.fillMaxSize(), state) {
+                items(itemsCount) {
+                    Box(Modifier.size(20.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            itemsCount = 100
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+        }
+    }
+
+    @Test
+    fun restoredScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+        lateinit var state: LazyListState
+        var itemsCount = 100
+        val recomposeCounter = mutableStateOf(0)
+        val tester = StateRestorationTester(rule)
+        tester.setContent {
+            state = rememberLazyListState()
+            LazyRow(Modifier.fillMaxSize(), state) {
+                recomposeCounter.value
+                items(itemsCount) {
+                    Box(Modifier.size(20.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollToItem(2, 10)
+            }
+            itemsCount = 0
+        }
+
+        tester.emulateSavedInstanceStateRestore()
+
+        rule.runOnIdle {
+            itemsCount = 100
+            recomposeCounter.value = 1
+        }
+
+        rule.runOnIdle {
+            assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+            assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+        }
+    }
+
     private fun LazyListState.scrollBy(offset: Dp) {
         runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
             animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap())
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
index 00401f3..58285ba 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
@@ -88,44 +88,9 @@
 
         val impl = Impl(
             topStart = CornerSize(10.0f),
-            topEnd = CornerSize(6.dp),
-            bottomEnd = CornerSize(1.0f),
-            bottomStart = CornerSize(2.0f),
-            onOutlineRequested = assertSizes
-        )
-
-        impl.createOutline(sizeWithLargerWidth, LayoutDirection.Ltr, density)
-        impl.createOutline(sizeWithLargerHeight, LayoutDirection.Ltr, density)
-
-        assertThat(sizesList).isEqualTo(mutableListOf(sizeWithLargerWidth, sizeWithLargerHeight))
-    }
-
-    @Test
-    fun largerBottomCornersUseRemainingFromMinDimensionSize() {
-        val density = Density(2f, 1f)
-        val sizeWithLargerWidth = Size(6.0f, 4.0f)
-        val sizeWithLargerHeight = Size(4.0f, 6.0f)
-
-        val sizesList = mutableListOf<Size>()
-        val assertSizes = { size: Size,
-            topStart: Float,
-            topEnd: Float,
-            bottomEnd: Float,
-            bottomStart: Float,
-            ld: LayoutDirection ->
-            sizesList.add(size)
-            assertThat(topStart).isEqualTo(1.0f)
-            assertThat(topEnd).isEqualTo(1.0f)
-            assertThat(bottomEnd).isEqualTo(3.0f)
-            assertThat(bottomStart).isEqualTo(3.0f)
-            assertThat(ld).isEqualTo(LayoutDirection.Ltr)
-        }
-
-        val impl = Impl(
-            topStart = CornerSize(1.0f),
-            topEnd = CornerSize(0.5f.dp),
-            bottomEnd = CornerSize(10f),
-            bottomStart = CornerSize(100),
+            topEnd = CornerSize(10.0f),
+            bottomEnd = CornerSize(0f),
+            bottomStart = CornerSize(0f),
             onOutlineRequested = assertSizes
         )
 
@@ -221,6 +186,76 @@
 
         assertThat(assertionExecuted).isTrue()
     }
+
+    @Test
+    fun overSizedEqualCornerSizes() {
+        val density = Density(2f, 1f)
+        val sizeWithLargerWidth = Size(6.0f, 4.0f)
+        val sizeWithLargerHeight = Size(4.0f, 6.0f)
+
+        val sizesList = mutableListOf<Size>()
+        val assertSizes = { size: Size,
+            topStart: Float,
+            topEnd: Float,
+            bottomEnd: Float,
+            bottomStart: Float,
+            ld: LayoutDirection ->
+            sizesList.add(size)
+            assertThat(topStart).isEqualTo(2.0f)
+            assertThat(topEnd).isEqualTo(2.0f)
+            assertThat(bottomEnd).isEqualTo(2.0f)
+            assertThat(bottomStart).isEqualTo(2.0f)
+            assertThat(ld).isEqualTo(LayoutDirection.Ltr)
+        }
+
+        val impl = Impl(
+            topStart = CornerSize(75),
+            topEnd = CornerSize(75),
+            bottomEnd = CornerSize(75),
+            bottomStart = CornerSize(75),
+            onOutlineRequested = assertSizes
+        )
+
+        impl.createOutline(sizeWithLargerWidth, LayoutDirection.Ltr, density)
+        impl.createOutline(sizeWithLargerHeight, LayoutDirection.Ltr, density)
+
+        assertThat(sizesList).isEqualTo(mutableListOf(sizeWithLargerWidth, sizeWithLargerHeight))
+    }
+
+    @Test
+    fun overSizedCornerSizesShouldProportionallyScale() {
+        val density = Density(2f, 1f)
+        val sizeWithLargerWidth = Size(15.0f, 10.0f)
+        val sizeWithLargerHeight = Size(10.0f, 15.0f)
+
+        val sizesList = mutableListOf<Size>()
+        val assertSizes = { size: Size,
+            topStart: Float,
+            topEnd: Float,
+            bottomEnd: Float,
+            bottomStart: Float,
+            ld: LayoutDirection ->
+            sizesList.add(size)
+            assertThat(topStart).isEqualTo(7.5f)
+            assertThat(topEnd).isEqualTo(2.5f)
+            assertThat(bottomEnd).isEqualTo(7.5f)
+            assertThat(bottomStart).isEqualTo(2.5f)
+            assertThat(ld).isEqualTo(LayoutDirection.Ltr)
+        }
+
+        val impl = Impl(
+            topStart = CornerSize(90),
+            topEnd = CornerSize(30),
+            bottomEnd = CornerSize(90),
+            bottomStart = CornerSize(30),
+            onOutlineRequested = assertSizes
+        )
+
+        impl.createOutline(sizeWithLargerWidth, LayoutDirection.Ltr, density)
+        impl.createOutline(sizeWithLargerHeight, LayoutDirection.Ltr, density)
+
+        assertThat(sizesList).isEqualTo(mutableListOf(sizeWithLargerWidth, sizeWithLargerHeight))
+    }
 }
 
 private class Impl(
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandlePopupPositionTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandlePopupPositionTest.kt
index 5141592..df97ada 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandlePopupPositionTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionHandlePopupPositionTest.kt
@@ -187,7 +187,7 @@
                             modifier = Modifier.onGloballyPositioned {
                                 measureLatch.countDown()
                             },
-                            handle = null
+                            content = null
                         )
                     }
                 }
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt
index 1eb2594..db0f0f6 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt
@@ -47,7 +47,7 @@
     directions: Pair<ResolvedTextDirection, ResolvedTextDirection>,
     handlesCrossed: Boolean,
     modifier: Modifier,
-    handle: (@Composable () -> Unit)?
+    content: @Composable (() -> Unit)?
 ) {
     SelectionHandlePopup(
         startHandlePosition = startHandlePosition,
@@ -56,14 +56,14 @@
         directions = directions,
         handlesCrossed = handlesCrossed
     ) {
-        if (handle == null) {
+        if (content == null) {
             DefaultSelectionHandle(
                 modifier = modifier,
                 isStartHandle = isStartHandle,
                 directions = directions,
                 handlesCrossed = handlesCrossed
             )
-        } else handle()
+        } else content()
     }
 }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
index 55f1ee3..4b7f5ee 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
@@ -40,7 +40,11 @@
 import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.layout.LayoutModifier
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
@@ -53,7 +57,9 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.verticalScrollAxisRange
 import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.launch
 import kotlin.math.roundToInt
 
@@ -294,7 +300,7 @@
             state = state
         )
         val layout = ScrollingLayoutModifier(state, reverseScrolling, isVertical)
-        semantics.then(scrolling).clipToBounds().then(layout)
+        semantics.then(scrolling).clipScrollableContainer(isVertical).then(layout)
     },
     inspectorInfo = debugInspectorInfo {
         name = "scroll"
@@ -340,19 +346,76 @@
 internal fun Constraints.assertNotNestingScrollableContainers(isVertical: Boolean) {
     if (isVertical) {
         check(maxHeight != Constraints.Infinity) {
-            "Nesting scrollable in the same direction layouts like ScrollableContainer and " +
-                "LazyColumn is not allowed. If you want to add a header before the list of" +
-                " items please take a look on LazyColumn component which has a DSL api which" +
+            "Nesting scrollable in the same direction layouts like LazyColumn and Column(Modifier" +
+                ".verticalScroll()) is not allowed. If you want to add a header before the list " +
+                "of items please take a look on LazyColumn component which has a DSL api which" +
                 " allows to first add a header via item() function and then the list of " +
                 "items via items()."
         }
     } else {
         check(maxWidth != Constraints.Infinity) {
-            "Nesting scrollable in the same direction layouts like ScrollableRow and " +
-                "LazyRow is not allowed. If you want to add a header before the list of " +
-                "items please take a look on LazyRow component which has a DSL api which " +
+            "Nesting scrollable in the same direction layouts like LazyRow and Row(Modifier" +
+                ".horizontalScroll() is not allowed. If you want to add a header before the list " +
+                "of items please take a look on LazyRow component which has a DSL api which " +
                 "allows to first add a fixed element via item() function and then the " +
                 "list of items via items()."
         }
     }
 }
+
+/**
+ * In the scrollable containers we want to clip the main axis sides in order to not display the
+ * content which is scrolled out. But once we apply clipToBounds() modifier on such containers it
+ * causes unexpected behavior as we also clip the content on the cross axis sides. It is
+ * unexpected as Compose components are not clipping by default. The most common case how it
+ * could be reproduced is a horizontally scrolling list of Cards. Cards have the elevation by
+ * default and such Cards will be drawn with clipped shadows on top and bottom. This was harder
+ * to reproduce in the Views system as usually scrolling containers like RecyclerView didn't have
+ * an opaque background which means the ripple was drawn on the surface on the first parent with
+ * background. In Compose as we don't clip by default we draw shadows right in place.
+ * We faced similar issue in Compose already with Androids Popups and Dialogs where we decided to
+ * just predefine some constant with a maximum elevation size we are not going to clip. We are
+ * going to reuse this technique here. This will improve how it works in most common cases. If the
+ * user will need to have a larger unclipped area for some reason they can always add the needed
+ * padding inside the scrollable area.
+ */
+internal fun Modifier.clipScrollableContainer(isVertical: Boolean) =
+    then(if (isVertical) VerticalScrollableClipModifier else HorizontalScrollableClipModifier)
+
+private val MaxSupportedElevation = 30.dp
+
+private val HorizontalScrollableClipModifier = Modifier.clip(object : Shape {
+    override fun createOutline(
+        size: Size,
+        layoutDirection: LayoutDirection,
+        density: Density
+    ): Outline {
+        val inflateSize = with(density) { MaxSupportedElevation.roundToPx().toFloat() }
+        return Outline.Rectangle(
+            Rect(
+                left = 0f,
+                top = -inflateSize,
+                right = size.width,
+                bottom = size.height + inflateSize
+            )
+        )
+    }
+})
+
+private val VerticalScrollableClipModifier = Modifier.clip(object : Shape {
+    override fun createOutline(
+        size: Size,
+        layoutDirection: LayoutDirection,
+        density: Density
+    ): Outline {
+        val inflateSize = with(density) { MaxSupportedElevation.roundToPx().toFloat() }
+        return Outline.Rectangle(
+            Rect(
+                left = -inflateSize,
+                top = 0f,
+                right = size.width + inflateSize,
+                bottom = size.height
+            )
+        )
+    }
+})
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index 141341d..f8d7ba5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.lazy
 
 import androidx.compose.foundation.assertNotNestingScrollableContainers
+import androidx.compose.foundation.clipScrollableContainer
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.scrollable
@@ -31,7 +32,6 @@
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.layout.SubcomposeLayout
 import androidx.compose.ui.layout.SubcomposeLayoutState
 import androidx.compose.ui.platform.LocalLayoutDirection
@@ -89,7 +89,7 @@
                 flingBehavior = flingBehavior,
                 state = state
             )
-            .clipToBounds()
+            .clipScrollableContainer(isVertical)
             .padding(contentPadding)
             .then(state.remeasurementModifier)
     ) { constraints ->
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 082c787..f592586 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -182,12 +182,7 @@
     }
 
     internal fun snapToItemIndexInternal(index: Int, scrollOffset: Int) {
-        scrollPosition.update(
-            index = DataIndex(index),
-            scrollOffset = scrollOffset,
-            // `true` will be replaced with the real value during the forceRemeasure() execution
-            canScrollForward = true
-        )
+        scrollPosition.update(DataIndex(index), scrollOffset)
         remeasurement.forceRemeasure()
     }
 
@@ -275,11 +270,7 @@
      *  Updates the state with the new calculated scroll position and consumed scroll.
      */
     internal fun applyMeasureResult(measureResult: LazyListMeasureResult) {
-        scrollPosition.update(
-            index = measureResult.firstVisibleItemIndex,
-            scrollOffset = measureResult.firstVisibleItemScrollOffset,
-            canScrollForward = measureResult.canScrollForward
-        )
+        scrollPosition.update(measureResult)
         lastVisibleItemIndexNonObservable = DataIndex(
             measureResult.visibleItemsInfo.lastOrNull()?.index ?: 0
         )
@@ -335,11 +326,27 @@
     private val scrollOffsetState = mutableStateOf(scrollOffset)
     val observableScrollOffset get() = scrollOffsetState.value
 
-    val canScrollBackward: Boolean get() = index.value != 0 || scrollOffset != 0
+    var canScrollBackward: Boolean = false
+        private set
     var canScrollForward: Boolean = false
         private set
 
-    fun update(index: DataIndex, scrollOffset: Int, canScrollForward: Boolean) {
+    private var hadFirstNotEmptyLayout = false
+
+    fun update(measureResult: LazyListMeasureResult) {
+        // we ignore the index and offset from measureResult until we get at least one
+        // measurement with real items. otherwise the initial index and scroll passed to the
+        // state would be lost and overridden with zeros.
+        if (hadFirstNotEmptyLayout || measureResult.totalItemsCount > 0) {
+            hadFirstNotEmptyLayout = true
+            update(measureResult.firstVisibleItemIndex, measureResult.firstVisibleItemScrollOffset)
+        }
+        this.canScrollForward = measureResult.canScrollForward
+        this.canScrollBackward = measureResult.firstVisibleItemIndex.value != 0 ||
+            measureResult.firstVisibleItemScrollOffset != 0
+    }
+
+    fun update(index: DataIndex, scrollOffset: Int) {
         require(index.value >= 0f) { "Index should be non-negative (${index.value})" }
         require(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
         if (index != this.index) {
@@ -350,7 +357,6 @@
             this.scrollOffset = scrollOffset
             scrollOffsetState.value = scrollOffset
         }
-        this.canScrollForward = canScrollForward
     }
 }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
index 2792bb6..710e524 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
@@ -21,7 +21,6 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
-import kotlin.math.min
 
 /**
  * Base class for [Shape]s defined by four [CornerSize]s.
@@ -45,11 +44,21 @@
         layoutDirection: LayoutDirection,
         density: Density
     ): Outline {
+        var topStart = topStart.toPx(size, density)
+        var topEnd = topEnd.toPx(size, density)
+        var bottomEnd = bottomEnd.toPx(size, density)
+        var bottomStart = bottomStart.toPx(size, density)
         val minDimension = size.minDimension
-        val topStart = min(topStart.toPx(size, density), minDimension)
-        val topEnd = min(topEnd.toPx(size, density), minDimension)
-        val bottomEnd = min(bottomEnd.toPx(size, density), minDimension - topEnd)
-        val bottomStart = min(bottomStart.toPx(size, density), minDimension - topStart)
+        if (topStart + bottomStart > minDimension) {
+            val scale = minDimension / (topStart + bottomStart)
+            topStart *= scale
+            bottomStart *= scale
+        }
+        if (topEnd + bottomEnd > minDimension) {
+            val scale = minDimension / (topEnd + bottomEnd)
+            topEnd *= scale
+            bottomEnd *= scale
+        }
         require(topStart >= 0.0f && topEnd >= 0.0f && bottomEnd >= 0.0f && bottomStart >= 0.0f) {
             "Corner size in Px can't be negative(topStart = $topStart, topEnd = $topEnd, " +
                 "bottomEnd = $bottomEnd, bottomStart = $bottomStart)!"
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index c0c452c..4d4e184 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -372,11 +372,17 @@
             state.layoutCoordinates?.let {
                 if (!it.isAttached) return
 
-                selectionRegistrar?.notifySelectionUpdateStart(
-                    layoutCoordinates = it,
-                    startPosition = startPoint,
-                    adjustment = SelectionAdjustment.WORD
-                )
+                if (outOfBoundary(startPoint, startPoint)) {
+                    selectionRegistrar?.notifySelectionUpdateSelectAll(
+                        selectableId = state.selectableId
+                    )
+                } else {
+                    selectionRegistrar?.notifySelectionUpdateStart(
+                        layoutCoordinates = it,
+                        startPosition = startPoint,
+                        adjustment = SelectionAdjustment.WORD
+                    )
+                }
 
                 dragBeginPosition = startPoint
             }
@@ -394,12 +400,14 @@
 
                 dragTotalDistance += delta
 
-                selectionRegistrar?.notifySelectionUpdate(
-                    layoutCoordinates = it,
-                    startPosition = dragBeginPosition,
-                    endPosition = dragBeginPosition + dragTotalDistance,
-                    adjustment = SelectionAdjustment.CHARACTER
-                )
+                if (!outOfBoundary(dragBeginPosition, dragBeginPosition + dragTotalDistance)) {
+                    selectionRegistrar?.notifySelectionUpdate(
+                        layoutCoordinates = it,
+                        startPosition = dragBeginPosition,
+                        endPosition = dragBeginPosition + dragTotalDistance,
+                        adjustment = SelectionAdjustment.CHARACTER
+                    )
+                }
             }
         }
 
@@ -484,6 +492,18 @@
         }
     }
 
+    private fun outOfBoundary(start: Offset, end: Offset): Boolean {
+        state.layoutResult?.let {
+            val lastOffset = it.layoutInput.text.text.length
+            val rawStartOffset = it.getOffsetForPosition(start)
+            val rawEndOffset = it.getOffsetForPosition(end)
+
+            return rawStartOffset >= lastOffset - 1 && rawEndOffset >= lastOffset - 1 ||
+                rawStartOffset < 0 && rawEndOffset < 0
+        }
+        return false
+    }
+
     /**
      * Draw the given selection on the canvas.
      */
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
index 84c367d..eb1704b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
@@ -57,6 +57,18 @@
         )
     }
 
+    override fun getSelectAllSelection(): Selection? {
+        val textLayoutResult = layoutResultCallback() ?: return null
+
+        return getAssembledSelectionInfo(
+            startOffset = 0,
+            endOffset = textLayoutResult.layoutInput.text.length,
+            handlesCrossed = false,
+            selectableId = selectableId,
+            textLayoutResult = textLayoutResult
+        )
+    }
+
     override fun getHandlePosition(selection: Selection, isStartHandle: Boolean): Offset {
         // Check if the selection handles's selectable is the current selectable.
         if (isStartHandle && selection.start.selectableId != this.selectableId ||
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt
index 0e33360..ebad9c2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt
@@ -60,6 +60,15 @@
     ): Selection?
 
     /**
+     * Returns selectAll [Selection] information for a selectable composable. If no selection can be
+     * provided null should be returned.
+     *
+     * @return selectAll [Selection] information for a selectable composable. If no selection can be
+     * provided null should be returned.
+     */
+    fun getSelectAllSelection(): Selection?
+
+    /**
      * Return the [Offset] of a [SelectionHandle].
      *
      * @param selection [Selection] contains the [SelectionHandle]
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
index d3da4b5..da2315f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
@@ -113,7 +113,7 @@
                             modifier = Modifier.pointerInput(observer) {
                                 detectDragGesturesWithObserver(observer)
                             },
-                            handle = null
+                            content = null
                         )
                     }
                 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.kt
index 390eecd..245e720 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.kt
@@ -33,7 +33,7 @@
     directions: Pair<ResolvedTextDirection, ResolvedTextDirection>,
     handlesCrossed: Boolean,
     modifier: Modifier,
-    handle: (@Composable () -> Unit)?
+    content: @Composable (() -> Unit)?
 )
 
 /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
index 25e8c89..1774fd4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
@@ -203,6 +203,21 @@
                 hideSelectionToolbar()
             }
 
+        selectionRegistrar.onSelectionUpdateSelectAll =
+            { selectableId ->
+                val (newSelection, newSubselection) = mergeSelections(
+                    selectableId = selectableId,
+                    previousSelection = selection,
+                )
+                if (newSelection != selection) {
+                    selectionRegistrar.subselections = newSubselection
+                    onSelectionChange(newSelection)
+                }
+
+                focusRequester.requestFocus()
+                hideSelectionToolbar()
+            }
+
         selectionRegistrar.onSelectionUpdateCallback =
             { layoutCoordinates, startPosition, endPosition, selectionMode ->
                 val startPositionOrCurrent = if (startPosition == null) {
@@ -356,6 +371,24 @@
         return Pair(newSelection, subselections)
     }
 
+    internal fun mergeSelections(
+        previousSelection: Selection? = null,
+        selectableId: Long
+    ): Pair<Selection?, Map<Long, Selection>> {
+        val subselections = mutableMapOf<Long, Selection>()
+        val newSelection = selectionRegistrar.sort(requireContainerCoordinates())
+            .fastFold(null) { mergedSelection: Selection?, selectable: Selectable ->
+                val selection = if (selectable.selectableId == selectableId)
+                    selectable.getSelectAllSelection() else null
+                selection?.let { subselections[selectable.selectableId] = it }
+                merge(mergedSelection, selection)
+            }
+        if (previousSelection != newSelection) hapticFeedBack?.performHapticFeedback(
+            HapticFeedbackType.TextHandleMove
+        )
+        return Pair(newSelection, subselections)
+    }
+
     internal fun getSelectedText(): AnnotatedString? {
         val selectables = selectionRegistrar.sort(requireContainerCoordinates())
         var selectedText: AnnotatedString? = null
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
index f9bc6af..554371a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
@@ -97,6 +97,14 @@
     )
 
     /**
+     * Call this method to notify the [SelectionContainer] that the selection has been initiated
+     * with selectAll [Selection].
+     *
+     * @param selectableId [selectableId] of the [Selectable]
+     */
+    fun notifySelectionUpdateSelectAll(selectableId: Long)
+
+    /**
      * Call this method to notify the [SelectionContainer] that  the selection has been updated.
      * The caller of this method should make sure that [notifySelectionUpdateStart] is always
      * called once before calling this function. And [notifySelectionUpdateEnd] is always called
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
index 143361f..7aea413 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
@@ -69,6 +69,13 @@
     )? = null
 
     /**
+     * The callback to be invoked when the selection is initiated with selectAll [Selection].
+     */
+    internal var onSelectionUpdateSelectAll: (
+        (Long) -> Unit
+    )? = null
+
+    /**
      * The callback to be invoked when the selection is updated.
      * If the first offset is null it means that the start of selection is unknown for the caller.
      */
@@ -170,6 +177,10 @@
         onSelectionUpdateStartCallback?.invoke(layoutCoordinates, startPosition, adjustment)
     }
 
+    override fun notifySelectionUpdateSelectAll(selectableId: Long) {
+        onSelectionUpdateSelectAll?.invoke(selectableId)
+    }
+
     override fun notifySelectionUpdate(
         layoutCoordinates: LayoutCoordinates,
         endPosition: Offset,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
index f5394fd..ef98a61 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
@@ -682,7 +682,7 @@
         modifier = Modifier.pointerInput(observer) {
             detectDragGesturesWithObserver(observer)
         },
-        handle = null
+        content = null
     )
 }
 
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.desktop.kt
index 06ab86b..e93a15e 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.desktop.kt
@@ -29,7 +29,7 @@
     directions: Pair<ResolvedTextDirection, ResolvedTextDirection>,
     handlesCrossed: Boolean,
     modifier: Modifier,
-    handle: (@Composable () -> Unit)?
+    content: (@Composable () -> Unit)?
 ) {
     // TODO
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
index 0444795..9c742d1 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
@@ -22,7 +22,16 @@
 import androidx.compose.foundation.text.selection.SelectionRegistrarImpl
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextLayoutInput
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.style.ResolvedTextDirection
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
 import com.nhaarman.mockitokotlin2.doReturn
 import com.nhaarman.mockitokotlin2.mock
 import com.nhaarman.mockitokotlin2.spy
@@ -70,6 +79,22 @@
 
         state = TextState(mock(), selectableId)
         state.layoutCoordinates = layoutCoordinates
+        state.layoutResult = TextLayoutResult(
+            TextLayoutInput(
+                text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
+                style = TextStyle(),
+                placeholders = listOf(),
+                maxLines = 1,
+                softWrap = true,
+                overflow = TextOverflow.Ellipsis,
+                density = Density(1.0f),
+                layoutDirection = LayoutDirection.Ltr,
+                resourceLoader = mock(),
+                constraints = Constraints.fixedWidth(100)
+            ),
+            multiParagraph = mock(),
+            size = IntSize(50, 50)
+        )
 
         val controller = TextController(state).also {
             it.update(selectionRegistrar)
@@ -80,6 +105,7 @@
     @Test
     fun longPressDragObserver_onLongPress_calls_notifySelectionInitiated() {
         val position = Offset(100f, 100f)
+        whenever(state.layoutResult?.getOffsetForPosition(position)).thenReturn("Hello".length)
 
         gesture.onStart(position)
 
@@ -91,12 +117,34 @@
     }
 
     @Test
+    fun longPressDragObserver_onLongPress_out_of_boundary_calls_notifySelectionUpdateSelectAll() {
+        val position = Offset(100f, 100f)
+        whenever(state.layoutResult?.getOffsetForPosition(position))
+            .thenReturn("Hello, World".length)
+
+        gesture.onStart(position)
+
+        verify(selectionRegistrar, times(1)).notifySelectionUpdateSelectAll(
+            selectableId = selectableId
+        )
+    }
+
+    @Test
     fun longPressDragObserver_onDragStart_reset_dragTotalDistance() {
         // Setup. Make sure selectionManager.dragTotalDistance is not 0.
         val dragDistance1 = Offset(15f, 10f)
         val beginPosition1 = Offset(30f, 20f)
         val dragDistance2 = Offset(100f, 300f)
         val beginPosition2 = Offset(300f, 200f)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition1))
+            .thenReturn("Hello".length)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition1 + dragDistance1))
+            .thenReturn("Hello".length)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition2))
+            .thenReturn("Hello".length)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition2 + dragDistance2))
+            .thenReturn("Hello".length)
+
         gesture.onStart(beginPosition1)
         gesture.onDrag(dragDistance1)
         // Setup. Cancel selection and reselect.
@@ -122,6 +170,10 @@
     fun longPressDragObserver_onDrag_calls_notifySelectionDrag() {
         val dragDistance = Offset(15f, 10f)
         val beginPosition = Offset(30f, 20f)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition))
+            .thenReturn("Hello".length)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition + dragDistance))
+            .thenReturn("Hello".length)
         gesture.onStart(beginPosition)
         selectionRegistrar.subselections = mapOf(selectableId to fakeSelection)
 
@@ -136,6 +188,27 @@
     }
 
     @Test
+    fun longPressDragObserver_onDrag_out_of_boundary_not_call_notifySelectionDrag() {
+        val dragDistance = Offset(15f, 10f)
+        val beginPosition = Offset(30f, 20f)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition))
+            .thenReturn("Hello, World".length)
+        whenever(state.layoutResult?.getOffsetForPosition(beginPosition + dragDistance))
+            .thenReturn("Hello, World".length)
+        gesture.onStart(beginPosition)
+        selectionRegistrar.subselections = mapOf(selectableId to fakeSelection)
+
+        gesture.onDrag(dragDistance)
+        verify(selectionRegistrar, times(0))
+            .notifySelectionUpdate(
+                layoutCoordinates = layoutCoordinates,
+                startPosition = beginPosition,
+                endPosition = beginPosition + dragDistance,
+                adjustment = SelectionAdjustment.CHARACTER
+            )
+    }
+
+    @Test
     fun longPressDragObserver_onStop_calls_notifySelectionEnd() {
         val beginPosition = Offset(30f, 20f)
         gesture.onStart(beginPosition)
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
index 9c480e5..b30cf4a 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
@@ -210,6 +210,20 @@
     var selectionToReturn: Selection? = null
     var textToReturn: AnnotatedString? = null
 
+    private val selectableKey = 1L
+    private val fakeSelectAllSelection: Selection = Selection(
+        start = Selection.AnchorInfo(
+            direction = ResolvedTextDirection.Ltr,
+            offset = 0,
+            selectableId = selectableKey
+        ),
+        end = Selection.AnchorInfo(
+            direction = ResolvedTextDirection.Ltr,
+            offset = 10,
+            selectableId = selectableKey
+        )
+    )
+
     override fun getSelection(
         startPosition: Offset,
         endPosition: Offset,
@@ -228,6 +242,10 @@
         return selectionToReturn
     }
 
+    override fun getSelectAllSelection(): Selection? {
+        return fakeSelectAllSelection
+    }
+
     override fun getText(): AnnotatedString {
         getTextCalledTimes++
         return textToReturn!!
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
index fa5092d..59736b1 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
@@ -193,6 +193,26 @@
     }
 
     @Test
+    fun mergeSelections_selectAll() {
+        val anotherSelectableId = 100L
+        val selectableAnother = mock<Selectable>()
+        whenever(selectableAnother.selectableId).thenReturn(anotherSelectableId)
+
+        selectionRegistrar.subscribe(selectableAnother)
+
+        selectionManager.mergeSelections(
+            selectableId = selectableId,
+            previousSelection = fakeSelection
+        )
+
+        verify(selectableAnother, times(0)).getSelectAllSelection()
+        verify(
+            hapticFeedback,
+            times(1)
+        ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
+    }
+
+    @Test
     fun getSelectedText_selection_null_return_null() {
         selectionManager.selection = null
 
diff --git a/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt b/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
index c228f39..4f83cc8 100644
--- a/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
+++ b/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
@@ -222,7 +222,7 @@
             if (hostView.hasFocus()) {
                 if (hostView.isFocused) {
                     // One of the Compose components has focus.
-                    focusManager.clearFocus(forcedClear = true)
+                    focusManager.clearFocus(force = true)
                 } else {
                     // A child view has focus. (View interop scenario).
                     // We could also use hostViewGroup.focusedChild?.clearFocus(), but the
diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt b/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt
index e2efcce..fb9ee75 100644
--- a/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt
+++ b/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt
@@ -16,13 +16,15 @@
 
 package androidx.compose.lint
 
-import com.intellij.psi.impl.compiled.ClsMethodImpl
+import com.intellij.psi.PsiMethod
 import com.intellij.psi.impl.compiled.ClsParameterImpl
+import com.intellij.psi.impl.light.LightParameter
 import kotlinx.metadata.jvm.annotations
 import org.jetbrains.kotlin.psi.KtAnnotated
 import org.jetbrains.kotlin.psi.KtFunction
 import org.jetbrains.kotlin.psi.KtProperty
 import org.jetbrains.kotlin.psi.KtTypeReference
+import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
 import org.jetbrains.uast.UAnnotation
 import org.jetbrains.uast.UAnonymousClass
 import org.jetbrains.uast.UCallExpression
@@ -35,7 +37,6 @@
 import org.jetbrains.uast.UVariable
 import org.jetbrains.uast.getContainingDeclaration
 import org.jetbrains.uast.getContainingUClass
-import org.jetbrains.uast.getContainingUMethod
 import org.jetbrains.uast.getParameterForArgument
 import org.jetbrains.uast.toUElement
 import org.jetbrains.uast.withContainingElements
@@ -110,14 +111,21 @@
  * Returns whether this parameter's type is @Composable or not
  */
 val UParameter.isComposable: Boolean
-    get() = when (sourcePsi) {
+    get() = when {
         // The parameter is in a class file. Currently type annotations aren't currently added to
         // the underlying type (https://youtrack.jetbrains.com/issue/KT-45307), so instead we use
         // the metadata annotation.
-        is ClsParameterImpl -> {
+        sourcePsi is ClsParameterImpl
+            // In some cases when a method is defined in bytecode and the call fails to resolve
+            // to the ClsMethodImpl, sourcePsi can be null. In this case we can instead use javaPsi
+            // which will have a light implementation. Note that javaPsi will return a light
+            // implementation for most Kotlin declarations too, so we need to first check to see if
+            // the sourcePsi is null.
+            // https://youtrack.jetbrains.com/issue/KT-46883
+            || (sourcePsi == null && javaPsi is LightParameter) -> {
             // Find the containing method, so we can get metadata from the containing class
-            val containingMethod = getContainingUMethod()!!.sourcePsi as ClsMethodImpl
-            val kmFunction = containingMethod.toKmFunction()
+            val containingMethod = javaPsi!!.getParentOfType<PsiMethod>(true)
+            val kmFunction = containingMethod!!.toKmFunction()
 
             val kmValueParameter = kmFunction?.valueParameters?.find {
                 it.name == name
@@ -236,10 +244,9 @@
     get() {
         if (type.hasAnnotation(Names.Runtime.Composable.javaFqn)) return true
 
-        // Annotations should be available on the PsiType itself in 1.4.30+, but we are
-        // currently on an older version of UAST / Kotlin embedded compiled
-        // (https://youtrack.jetbrains.com/issue/KT-45244), so we need to manually check the
-        // underlying type reference. Until then, the above check will always fail.
+        // Annotations on the types of local properties (val foo: @Composable () -> Unit = {})
+        // are currently not present on the PsiType, we so need to manually check the underlying
+        // type reference. (https://youtrack.jetbrains.com/issue/KT-45244)
         return (sourcePsi as? KtTypeReference)?.hasComposableAnnotation == true
     }
 
diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/KotlinMetadataUtils.kt b/compose/lint/common/src/main/java/androidx/compose/lint/KotlinMetadataUtils.kt
index c9da754..6e751b5 100644
--- a/compose/lint/common/src/main/java/androidx/compose/lint/KotlinMetadataUtils.kt
+++ b/compose/lint/common/src/main/java/androidx/compose/lint/KotlinMetadataUtils.kt
@@ -20,8 +20,8 @@
 import com.intellij.lang.jvm.annotation.JvmAnnotationAttributeValue
 import com.intellij.lang.jvm.annotation.JvmAnnotationConstantValue
 import com.intellij.psi.PsiAnnotation
+import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiMethod
-import com.intellij.psi.impl.compiled.ClsMemberImpl
 import com.intellij.psi.impl.compiled.ClsMethodImpl
 import com.intellij.psi.util.ClassUtil
 import kotlinx.metadata.KmDeclarationContainer
@@ -31,23 +31,24 @@
 import kotlinx.metadata.jvm.signature
 
 /**
- * @return the corresponding [KmFunction] for this [ClsMethodImpl], or `null` if there is no
- * corresponding [KmFunction].
+ * @return the corresponding [KmFunction] for this [PsiMethod], or `null` if there is no
+ * corresponding [KmFunction]. This method is only meaningful if this [PsiMethod] represents a
+ * method defined in bytecode (most often a [ClsMethodImpl]).
  */
-fun ClsMethodImpl.toKmFunction(): KmFunction? =
-    getKmDeclarationContainer()?.findKmFunctionForPsiMethod(this)
+fun PsiMethod.toKmFunction(): KmFunction? =
+    containingClass!!.getKmDeclarationContainer()?.findKmFunctionForPsiMethod(this)
 
 // TODO: https://youtrack.jetbrains.com/issue/KT-45310
 // Currently there is no built in support for parsing kotlin metadata from kotlin class files, so
 // we need to manually inspect the annotations and work with Cls* (compiled PSI).
 /**
- * Returns the [KmDeclarationContainer] using the kotlin.Metadata annotation present on the
- * surrounding class. Returns null if there is no surrounding annotation (not parsing a Kotlin
+ * Returns the [KmDeclarationContainer] using the kotlin.Metadata annotation present on this
+ * [PsiClass]. Returns null if there is no annotation (not parsing a Kotlin
  * class file), the annotation data is for an unsupported version of Kotlin, or if the metadata
  * represents a synthetic class.
  */
-private fun ClsMemberImpl<*>.getKmDeclarationContainer(): KmDeclarationContainer? {
-    val classKotlinMetadataAnnotation = containingClass?.annotations?.find {
+private fun PsiClass.getKmDeclarationContainer(): KmDeclarationContainer? {
+    val classKotlinMetadataAnnotation = annotations.find {
         // hasQualifiedName() not available on the min version of Lint we compile against
         it.qualifiedName == KotlinMetadataFqn
     } ?: return null
diff --git a/compose/material/material-ripple/api/1.0.0-beta08.txt b/compose/material/material-ripple/api/1.0.0-beta08.txt
index 171df53..3e18f6d 100644
--- a/compose/material/material-ripple/api/1.0.0-beta08.txt
+++ b/compose/material/material-ripple/api/1.0.0-beta08.txt
@@ -35,8 +35,5 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> getLocalRippleTheme();
   }
 
-  public final class Ripple_androidKt {
-  }
-
 }
 
diff --git a/compose/material/material-ripple/api/current.ignore b/compose/material/material-ripple/api/current.ignore
new file mode 100644
index 0000000..41f1904
--- /dev/null
+++ b/compose/material/material-ripple/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.compose.material.ripple.Ripple_androidKt:
+    Removed class androidx.compose.material.ripple.Ripple_androidKt
diff --git a/compose/material/material-ripple/api/current.txt b/compose/material/material-ripple/api/current.txt
index 171df53..3e18f6d 100644
--- a/compose/material/material-ripple/api/current.txt
+++ b/compose/material/material-ripple/api/current.txt
@@ -35,8 +35,5 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> getLocalRippleTheme();
   }
 
-  public final class Ripple_androidKt {
-  }
-
 }
 
diff --git a/compose/material/material-ripple/api/public_plus_experimental_1.0.0-beta08.txt b/compose/material/material-ripple/api/public_plus_experimental_1.0.0-beta08.txt
index 9c82a47..3e18f6d 100644
--- a/compose/material/material-ripple/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/material/material-ripple/api/public_plus_experimental_1.0.0-beta08.txt
@@ -1,9 +1,6 @@
 // Signature format: 4.0
 package androidx.compose.material.ripple {
 
-  @kotlin.RequiresOptIn(message="This ripple API is experimental and may change / be removed in the future.") public @interface ExperimentalRippleApi {
-  }
-
   @androidx.compose.runtime.Immutable public final class RippleAlpha {
     ctor public RippleAlpha(float draggedAlpha, float focusedAlpha, float hoveredAlpha, float pressedAlpha);
     method public float getDraggedAlpha();
@@ -38,9 +35,5 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> getLocalRippleTheme();
   }
 
-  public final class Ripple_androidKt {
-    method @androidx.compose.material.ripple.ExperimentalRippleApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalRippleNativeRendering();
-  }
-
 }
 
diff --git a/compose/material/material-ripple/api/public_plus_experimental_current.txt b/compose/material/material-ripple/api/public_plus_experimental_current.txt
index 9c82a47..3e18f6d 100644
--- a/compose/material/material-ripple/api/public_plus_experimental_current.txt
+++ b/compose/material/material-ripple/api/public_plus_experimental_current.txt
@@ -1,9 +1,6 @@
 // Signature format: 4.0
 package androidx.compose.material.ripple {
 
-  @kotlin.RequiresOptIn(message="This ripple API is experimental and may change / be removed in the future.") public @interface ExperimentalRippleApi {
-  }
-
   @androidx.compose.runtime.Immutable public final class RippleAlpha {
     ctor public RippleAlpha(float draggedAlpha, float focusedAlpha, float hoveredAlpha, float pressedAlpha);
     method public float getDraggedAlpha();
@@ -38,9 +35,5 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> getLocalRippleTheme();
   }
 
-  public final class Ripple_androidKt {
-    method @androidx.compose.material.ripple.ExperimentalRippleApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalRippleNativeRendering();
-  }
-
 }
 
diff --git a/compose/material/material-ripple/api/restricted_1.0.0-beta08.txt b/compose/material/material-ripple/api/restricted_1.0.0-beta08.txt
index 171df53..3e18f6d 100644
--- a/compose/material/material-ripple/api/restricted_1.0.0-beta08.txt
+++ b/compose/material/material-ripple/api/restricted_1.0.0-beta08.txt
@@ -35,8 +35,5 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> getLocalRippleTheme();
   }
 
-  public final class Ripple_androidKt {
-  }
-
 }
 
diff --git a/compose/material/material-ripple/api/restricted_current.ignore b/compose/material/material-ripple/api/restricted_current.ignore
new file mode 100644
index 0000000..41f1904
--- /dev/null
+++ b/compose/material/material-ripple/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.compose.material.ripple.Ripple_androidKt:
+    Removed class androidx.compose.material.ripple.Ripple_androidKt
diff --git a/compose/material/material-ripple/api/restricted_current.txt b/compose/material/material-ripple/api/restricted_current.txt
index 171df53..3e18f6d 100644
--- a/compose/material/material-ripple/api/restricted_current.txt
+++ b/compose/material/material-ripple/api/restricted_current.txt
@@ -35,8 +35,5 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> getLocalRippleTheme();
   }
 
-  public final class Ripple_androidKt {
-  }
-
 }
 
diff --git a/compose/material/material-ripple/src/androidAndroidTest/kotlin/androidx/compose/material/ripple/LocalRippleNativeRenderingTest.kt b/compose/material/material-ripple/src/androidAndroidTest/kotlin/androidx/compose/material/ripple/LocalRippleNativeRenderingTest.kt
deleted file mode 100644
index d376ebd..0000000
--- a/compose/material/material-ripple/src/androidAndroidTest/kotlin/androidx/compose/material/ripple/LocalRippleNativeRenderingTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2021 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.compose.material.ripple
-
-import androidx.compose.foundation.IndicationInstance
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.remember
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Test for [LocalRippleNativeRendering]
- */
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-class LocalRippleNativeRenderingTest {
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun usesNativeRipplesByDefault() {
-        lateinit var instance: IndicationInstance
-        rule.setContent {
-            val ripple = rememberRipple()
-            val interactionSource = remember { MutableInteractionSource() }
-            instance = ripple.rememberUpdatedInstance(interactionSource)
-        }
-
-        rule.runOnIdle {
-            Truth.assertThat(instance).isInstanceOf(AndroidRippleIndicationInstance::class.java)
-        }
-    }
-
-    @OptIn(ExperimentalRippleApi::class)
-    @Test
-    fun usesCommonRipples_whenLocalNativeRippleRenderIsSet() {
-        lateinit var instance: IndicationInstance
-        rule.setContent {
-            CompositionLocalProvider(LocalRippleNativeRendering provides false) {
-                val ripple = rememberRipple()
-                val interactionSource = remember { MutableInteractionSource() }
-                instance = ripple.rememberUpdatedInstance(interactionSource)
-            }
-        }
-
-        rule.runOnIdle {
-            Truth.assertThat(instance).isInstanceOf(CommonRippleIndicationInstance::class.java)
-        }
-    }
-}
diff --git a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
index 326c385..2c574e4 100644
--- a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
+++ b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
@@ -22,8 +22,6 @@
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocal
-import androidx.compose.runtime.ProvidableCompositionLocal
 import androidx.compose.runtime.RememberObserver
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
@@ -31,7 +29,6 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
@@ -57,7 +54,6 @@
     radius: Dp,
     color: State<Color>
 ) : Ripple(bounded, radius, color) {
-    @OptIn(ExperimentalRippleApi::class)
     @Composable
     override fun rememberUpdatedRippleInstance(
         interactionSource: InteractionSource,
@@ -67,9 +63,8 @@
         rippleAlpha: State<RippleAlpha>
     ): RippleIndicationInstance {
         val view = findNearestViewGroup()
-        // Fallback to drawing inside Compose if needed, using the common implementation
         // TODO(b/188112048): Remove isInEditMode once RenderThread support is fixed in Layoutlib.
-        if (!LocalRippleNativeRendering.current || view.isInEditMode) {
+        if (view.isInEditMode) {
             return remember(interactionSource, this) {
                 CommonRippleIndicationInstance(bounded, radius, color, rippleAlpha)
             }
@@ -108,34 +103,21 @@
      */
     @Composable
     private fun findNearestViewGroup(): ViewGroup {
-        var view: View? = LocalView.current
+        var view: View = LocalView.current
         while (view !is ViewGroup) {
+            val parent = view.parent
             // We should never get to a ViewParent that isn't a View, without finding a ViewGroup
-            // first
-            view = view?.parent as View
+            // first - throw an exception if we do.
+            require(parent is View) {
+                "Couldn't find a valid parent for $view. Are you overriding LocalView and " +
+                    "providing a View that is not attached to the view hierarchy?"
+            }
+            view = parent
         }
         return view
     }
 }
 
-@RequiresOptIn(
-    "This ripple API is experimental and may change / be removed in the future."
-)
-public annotation class ExperimentalRippleApi
-
-/**
- * [CompositionLocal] that configures whether native ripples are used to draw the ripple effect
- * inside components. If set to false, the ripples will be drawn inside Compose, consistent with
- * the behavior in previous Compose releases. This is a temporary API, and will be removed when
- * native ripples are stable in Compose.
- *
- * If you use this to avoid a bug with native ripples, please [file a bug](https://issuetracker.google.com/issues/new?component=612128)
- */
-@get:ExperimentalRippleApi
-@ExperimentalRippleApi
-public val LocalRippleNativeRendering: ProvidableCompositionLocal<Boolean> =
-    staticCompositionLocalOf { true }
-
 /**
  * Android specific [RippleIndicationInstance]. This uses a [RippleHostView] provided by
  * [rippleContainer] to draw ripples in the drawing bounds provided within [drawIndication].
diff --git a/compose/material/material/api/1.0.0-beta08.txt b/compose/material/material/api/1.0.0-beta08.txt
index 7cedeb6..ff9ea09 100644
--- a/compose/material/material/api/1.0.0-beta08.txt
+++ b/compose/material/material/api/1.0.0-beta08.txt
@@ -250,9 +250,22 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> getLocalElevationOverlay();
   }
 
-  public enum FabPosition {
-    enum_constant public static final androidx.compose.material.FabPosition Center;
-    enum_constant public static final androidx.compose.material.FabPosition End;
+  public final inline class FabPosition {
+    ctor public FabPosition();
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.material.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter-5ygKITE();
+    method public int getEnd-5ygKITE();
+    property public final int Center;
+    property public final int End;
   }
 
   public final class FloatingActionButtonDefaults {
@@ -366,7 +379,7 @@
   }
 
   public final class ScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
   }
 
diff --git a/compose/material/material/api/current.ignore b/compose/material/material/api/current.ignore
new file mode 100644
index 0000000..ae87caf
--- /dev/null
+++ b/compose/material/material/api/current.ignore
@@ -0,0 +1,13 @@
+// Baseline format: 1.0
+ChangedSuperclass: androidx.compose.material.FabPosition:
+    Class androidx.compose.material.FabPosition superclass changed from java.lang.Enum to java.lang.Object
+
+
+RemovedField: androidx.compose.material.FabPosition#Center:
+    Removed enum constant androidx.compose.material.FabPosition.Center
+RemovedField: androidx.compose.material.FabPosition#End:
+    Removed enum constant androidx.compose.material.FabPosition.End
+
+
+RemovedMethod: androidx.compose.material.ScaffoldKt#Scaffold-axyFlp8(androidx.compose.ui.Modifier, androidx.compose.material.ScaffoldState, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.material.FabPosition, boolean, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>, boolean, androidx.compose.ui.graphics.Shape, float, long, long, long, long, long, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit>):
+    Removed method androidx.compose.material.ScaffoldKt.Scaffold-axyFlp8(androidx.compose.ui.Modifier,androidx.compose.material.ScaffoldState,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.material.FabPosition,boolean,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>,boolean,androidx.compose.ui.graphics.Shape,float,long,long,long,long,long,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit>)
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 7cedeb6..ff9ea09 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -250,9 +250,22 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> getLocalElevationOverlay();
   }
 
-  public enum FabPosition {
-    enum_constant public static final androidx.compose.material.FabPosition Center;
-    enum_constant public static final androidx.compose.material.FabPosition End;
+  public final inline class FabPosition {
+    ctor public FabPosition();
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.material.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter-5ygKITE();
+    method public int getEnd-5ygKITE();
+    property public final int Center;
+    property public final int End;
   }
 
   public final class FloatingActionButtonDefaults {
@@ -366,7 +379,7 @@
   }
 
   public final class ScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
   }
 
diff --git a/compose/material/material/api/public_plus_experimental_1.0.0-beta08.txt b/compose/material/material/api/public_plus_experimental_1.0.0-beta08.txt
index 5dd28c8..57df28b 100644
--- a/compose/material/material/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/material/material/api/public_plus_experimental_1.0.0-beta08.txt
@@ -115,7 +115,7 @@
   }
 
   public final class BottomSheetScaffoldKt {
-    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold-0Ttp7_s(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomSheetScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional float sheetPeekHeight, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold-0Ttp7_s(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomSheetScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional int floatingActionButtonPosition, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional float sheetPeekHeight, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.BottomSheetState bottomSheetState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
   }
@@ -357,9 +357,22 @@
   @kotlin.RequiresOptIn(message="This material API is experimental and is likely to change or to be removed in" + " the future.") public @interface ExperimentalMaterialApi {
   }
 
-  public enum FabPosition {
-    enum_constant public static final androidx.compose.material.FabPosition Center;
-    enum_constant public static final androidx.compose.material.FabPosition End;
+  public final inline class FabPosition {
+    ctor public FabPosition();
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.material.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter-5ygKITE();
+    method public int getEnd-5ygKITE();
+    property public final int Center;
+    property public final int End;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FixedThreshold implements androidx.compose.material.ThresholdConfig {
@@ -506,7 +519,7 @@
   }
 
   public final class ScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
   }
 
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index 5dd28c8..57df28b 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -115,7 +115,7 @@
   }
 
   public final class BottomSheetScaffoldKt {
-    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold-0Ttp7_s(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomSheetScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional float sheetPeekHeight, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold-0Ttp7_s(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomSheetScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional int floatingActionButtonPosition, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional float sheetPeekHeight, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.BottomSheetState bottomSheetState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
     method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
   }
@@ -357,9 +357,22 @@
   @kotlin.RequiresOptIn(message="This material API is experimental and is likely to change or to be removed in" + " the future.") public @interface ExperimentalMaterialApi {
   }
 
-  public enum FabPosition {
-    enum_constant public static final androidx.compose.material.FabPosition Center;
-    enum_constant public static final androidx.compose.material.FabPosition End;
+  public final inline class FabPosition {
+    ctor public FabPosition();
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.material.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter-5ygKITE();
+    method public int getEnd-5ygKITE();
+    property public final int Center;
+    property public final int End;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FixedThreshold implements androidx.compose.material.ThresholdConfig {
@@ -506,7 +519,7 @@
   }
 
   public final class ScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
   }
 
diff --git a/compose/material/material/api/restricted_1.0.0-beta08.txt b/compose/material/material/api/restricted_1.0.0-beta08.txt
index 7cedeb6..ff9ea09 100644
--- a/compose/material/material/api/restricted_1.0.0-beta08.txt
+++ b/compose/material/material/api/restricted_1.0.0-beta08.txt
@@ -250,9 +250,22 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> getLocalElevationOverlay();
   }
 
-  public enum FabPosition {
-    enum_constant public static final androidx.compose.material.FabPosition Center;
-    enum_constant public static final androidx.compose.material.FabPosition End;
+  public final inline class FabPosition {
+    ctor public FabPosition();
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.material.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter-5ygKITE();
+    method public int getEnd-5ygKITE();
+    property public final int Center;
+    property public final int End;
   }
 
   public final class FloatingActionButtonDefaults {
@@ -366,7 +379,7 @@
   }
 
   public final class ScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
   }
 
diff --git a/compose/material/material/api/restricted_current.ignore b/compose/material/material/api/restricted_current.ignore
new file mode 100644
index 0000000..ae87caf
--- /dev/null
+++ b/compose/material/material/api/restricted_current.ignore
@@ -0,0 +1,13 @@
+// Baseline format: 1.0
+ChangedSuperclass: androidx.compose.material.FabPosition:
+    Class androidx.compose.material.FabPosition superclass changed from java.lang.Enum to java.lang.Object
+
+
+RemovedField: androidx.compose.material.FabPosition#Center:
+    Removed enum constant androidx.compose.material.FabPosition.Center
+RemovedField: androidx.compose.material.FabPosition#End:
+    Removed enum constant androidx.compose.material.FabPosition.End
+
+
+RemovedMethod: androidx.compose.material.ScaffoldKt#Scaffold-axyFlp8(androidx.compose.ui.Modifier, androidx.compose.material.ScaffoldState, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.material.FabPosition, boolean, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>, boolean, androidx.compose.ui.graphics.Shape, float, long, long, long, long, long, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit>):
+    Removed method androidx.compose.material.ScaffoldKt.Scaffold-axyFlp8(androidx.compose.ui.Modifier,androidx.compose.material.ScaffoldState,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.material.FabPosition,boolean,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>,boolean,androidx.compose.ui.graphics.Shape,float,long,long,long,long,long,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit>)
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 7cedeb6..ff9ea09 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -250,9 +250,22 @@
     method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> getLocalElevationOverlay();
   }
 
-  public enum FabPosition {
-    enum_constant public static final androidx.compose.material.FabPosition Center;
-    enum_constant public static final androidx.compose.material.FabPosition End;
+  public final inline class FabPosition {
+    ctor public FabPosition();
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.material.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter-5ygKITE();
+    method public int getEnd-5ygKITE();
+    property public final int Center;
+    property public final int End;
   }
 
   public final class FloatingActionButtonDefaults {
@@ -366,7 +379,7 @@
   }
 
   public final class ScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional androidx.compose.material.FabPosition floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Scaffold-axyFlp8(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
   }
 
diff --git a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt
index bb03014..9daa0c4 100644
--- a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt
+++ b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconGenerationTask.kt
@@ -170,6 +170,9 @@
             // b/175401659 - disable lint as it takes a long time, and most errors should
             // be caught by lint on material-icons-core anyway
             project.afterEvaluate {
+                project.tasks.named("lintAnalyzeDebug") { t ->
+                    t.enabled = false
+                }
                 project.tasks.named("lintDebug") { t ->
                     t.enabled = false
                 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
index dbda4a7..c8d1bd0 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -369,6 +370,23 @@
     }
 
     @Test
+    fun slider_min_size() {
+        rule.setMaterialContent {
+            Box(Modifier.requiredSize(0.dp)) {
+                Slider(
+                    modifier = Modifier.testTag(tag),
+                    value = 0f,
+                    onValueChange = { }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(tag)
+            .assertWidthIsEqualTo(ThumbRadius * 2)
+            .assertHeightIsEqualTo(ThumbRadius * 2)
+    }
+
+    @Test
     fun slider_noUnwantedCallbackCalls() {
         val state = mutableStateOf(0f)
         val callCount = mutableStateOf(0f)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
index acce578..ad8e5d9 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
@@ -406,7 +406,7 @@
 
             val fabOffsetX = when (floatingActionButtonPosition) {
                 FabPosition.Center -> (placeable.width - fabPlaceable.width) / 2
-                FabPosition.End -> placeable.width - fabPlaceable.width - FabEndSpacing.roundToPx()
+                else -> placeable.width - fabPlaceable.width - FabEndSpacing.roundToPx()
             }
             val fabOffsetY = sheetOffsetY - fabPlaceable.height / 2
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index c261b7d..0c38d82 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -69,19 +69,28 @@
 /**
  * The possible positions for a [FloatingActionButton] attached to a [Scaffold].
  */
-enum class FabPosition {
-    /**
-     * Position FAB at the bottom of the screen in the center, above the [BottomAppBar] (if it
-     * exists)
-     */
+@Suppress("INLINE_CLASS_DEPRECATED", "EXPERIMENTAL_FEATURE_WARNING")
+inline class FabPosition internal constructor(val value: Int) {
+    companion object {
+        /**
+         * Position FAB at the bottom of the screen in the center, above the [BottomAppBar] (if it
+         * exists)
+         */
+        val Center = FabPosition(0)
 
-    Center,
+        /**
+         * Position FAB at the bottom of the screen at the end, above the [BottomAppBar] (if it
+         * exists)
+         */
+        val End = FabPosition(1)
+    }
 
-    /**
-     * Position FAB at the bottom of the screen at the end, above the [BottomAppBar] (if it
-     * exists)
-     */
-    End
+    override fun toString(): String {
+        return when (this) {
+            Center -> "FabPosition.Center"
+            else -> "FabPosition.End"
+        }
+    }
 }
 
 /**
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index f2a1a77..090084f 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -39,6 +39,7 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSizeIn
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.widthIn
 import androidx.compose.foundation.progressSemantics
@@ -140,7 +141,9 @@
         if (steps == 0) emptyList() else List(steps + 2) { it.toFloat() / (steps + 1) }
     }
     BoxWithConstraints(
-        modifier.sliderSemantics(value, tickFractions, enabled, onValueChange, valueRange, steps)
+        modifier
+            .requiredSizeIn(minWidth = ThumbRadius * 2, minHeight = ThumbRadius * 2)
+            .sliderSemantics(value, tickFractions, enabled, onValueChange, valueRange, steps)
     ) {
         val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
         val maxPx = constraints.maxWidth.toFloat()
diff --git a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
index 0a9f875..b077db6 100644
--- a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
+++ b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
@@ -24,8 +24,8 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import androidx.compose.ui.window.DialogProperties
+import androidx.compose.ui.window.v1.Dialog
+import androidx.compose.ui.window.v1.DialogProperties
 
 /**
  * Alert dialog is a [Dialog] which interrupts the user with urgent information, details or actions.
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt
index ad32a95..cd738fe 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetector.kt
@@ -135,25 +135,31 @@
 
     companion object {
         val ComposableLambdaParameterNaming = Issue.create(
-            "ComposableLambdaParameterNaming",
-            "Primary composable lambda parameter not named `content`",
-            "Composable functions with only one composable lambda parameter should use the name " +
-                "`content` for the parameter.",
-            Category.CORRECTNESS, 3, Severity.IGNORE,
-            Implementation(
+            id = "ComposableLambdaParameterNaming",
+            briefDescription = "Primary composable lambda parameter not named `content`",
+            explanation = "Composable functions with only one composable lambda parameter should " +
+                "use the name `content` for the parameter.",
+            category = Category.CORRECTNESS,
+            priority = 3,
+            severity = Severity.WARNING,
+            enabledByDefault = false,
+            implementation = Implementation(
                 ComposableLambdaParameterDetector::class.java,
                 EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
             )
         )
 
         val ComposableLambdaParameterPosition = Issue.create(
-            "ComposableLambdaParameterPosition",
-            "Non-trailing primary composable lambda parameter",
-            "Composable functions with only one composable lambda parameter should place the " +
-                "parameter at the end of the parameter list, so it can be used as a trailing " +
-                "lambda.",
-            Category.CORRECTNESS, 3, Severity.IGNORE,
-            Implementation(
+            id = "ComposableLambdaParameterPosition",
+            briefDescription = "Non-trailing primary composable lambda parameter",
+            explanation = "Composable functions with only one composable lambda parameter should " +
+                "place the parameter at the end of the parameter list, so it can be used as a " +
+                "trailing lambda.",
+            category = Category.CORRECTNESS,
+            priority = 3,
+            severity = Severity.WARNING,
+            enabledByDefault = false,
+            implementation = Implementation(
                 ComposableLambdaParameterDetector::class.java,
                 EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
             )
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetectorTest.kt
index da08c10..cae7ccf 100644
--- a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetectorTest.kt
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ComposableLambdaParameterDetectorTest.kt
@@ -214,7 +214,12 @@
                 @Composable
                 fun Button(
                     text: @Composable (() -> Unit)?,
-                    icon: @Composable () -> Unit,
+                    foo: Int
+                ) {}
+
+                @Composable
+                fun Button2(
+                    text: (@Composable () -> Unit)?,
                     foo: Int
                 ) {}
             """
@@ -222,7 +227,35 @@
             kotlin(Stubs.Composable)
         )
             .run()
-            .expectClean()
+            .expect(
+                """
+src/androidx/compose/ui/foo/test.kt:8: Warning: Composable lambda parameter should be named content [ComposableLambdaParameterNaming]
+                    text: @Composable (() -> Unit)?,
+                    ~~~~
+src/androidx/compose/ui/foo/test.kt:14: Warning: Composable lambda parameter should be named content [ComposableLambdaParameterNaming]
+                    text: (@Composable () -> Unit)?,
+                    ~~~~
+src/androidx/compose/ui/foo/test.kt:8: Warning: Composable lambda parameter should be the last parameter so it can be used as a trailing lambda [ComposableLambdaParameterPosition]
+                    text: @Composable (() -> Unit)?,
+                    ~~~~
+src/androidx/compose/ui/foo/test.kt:14: Warning: Composable lambda parameter should be the last parameter so it can be used as a trailing lambda [ComposableLambdaParameterPosition]
+                    text: (@Composable () -> Unit)?,
+                    ~~~~
+0 errors, 4 warnings
+            """
+            )
+            .expectFixDiffs(
+                """
+Fix for src/androidx/compose/ui/foo/test.kt line 8: Rename text to content:
+@@ -8 +8
+-                     text: @Composable (() -> Unit)?,
++                     content: @Composable (() -> Unit)?,
+Fix for src/androidx/compose/ui/foo/test.kt line 14: Rename text to content:
+@@ -14 +14
+-                     text: (@Composable () -> Unit)?,
++                     content: (@Composable () -> Unit)?,
+            """
+            )
     }
 
     @Test
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/build.gradle b/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
index 460dabe..2bcb7e7 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
@@ -56,3 +56,7 @@
     androidTestImplementation("androidx.activity:activity:1.2.0")
     androidTestImplementation(project(":activity:activity-compose"))
 }
+
+androidx {
+    benchmarkRunAlsoInterpreted = true
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index e8ff224..76af048 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -3081,9 +3081,10 @@
         value: Int,
         noinline block: T.(value: Int) -> Unit
     ) = with(composer) {
+        val inserting = inserting
         if (inserting || rememberedValue() != value) {
             updateRememberedValue(value)
-            composer.apply(value, block)
+            if (!inserting) apply(value, block)
         }
     }
 
@@ -3102,9 +3103,10 @@
         value: V,
         block: T.(value: V) -> Unit
     ) = with(composer) {
+        val inserting = inserting
         if (inserting || rememberedValue() != value) {
             updateRememberedValue(value)
-            composer.apply(value, block)
+            if (!inserting) apply(value, block)
         }
     }
 
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
index 7e07357..bc34c6e 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
@@ -2944,6 +2944,65 @@
         stateA++
         advance()
     }
+
+    /**
+     * set should set the value every time, update should only set after initial composition.
+     */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun composeNodeSetVsUpdate() = runBlockingTest {
+        localRecomposerTest { recomposer ->
+            class SetUpdateNode(property: String) {
+                var changeCount = 0
+                var property: String = property
+                    set(value) {
+                        field = value
+                        changeCount++
+                    }
+            }
+            class SetUpdateNodeApplier : AbstractApplier<SetUpdateNode>(SetUpdateNode("root")) {
+                override fun insertTopDown(index: Int, instance: SetUpdateNode) {}
+                override fun insertBottomUp(index: Int, instance: SetUpdateNode) {}
+                override fun remove(index: Int, count: Int) {}
+                override fun move(from: Int, to: Int, count: Int) {}
+                override fun onClear() {}
+            }
+            val composition = Composition(SetUpdateNodeApplier(), recomposer)
+            val nodes = mutableListOf<SetUpdateNode>()
+            fun makeNode(property: String) = SetUpdateNode(property).also { nodes += it }
+
+            var value by mutableStateOf("initial")
+
+            composition.setContent {
+                ComposeNode<SetUpdateNode, SetUpdateNodeApplier>(
+                    factory = { makeNode(value) },
+                    update = {
+                        set(value) { property = value }
+                    }
+                )
+                ComposeNode<SetUpdateNode, SetUpdateNodeApplier>(
+                    factory = { makeNode(value) },
+                    update = {
+                        update(value) { property = value }
+                    }
+                )
+            }
+
+            assertEquals("initial", nodes[0].property, "node 0 initial composition value")
+            assertEquals("initial", nodes[1].property, "node 1 initial composition value")
+            assertEquals(1, nodes[0].changeCount, "node 0 initial composition changeCount")
+            assertEquals(0, nodes[1].changeCount, "node 1 initial composition changeCount")
+
+            value = "changed"
+            Snapshot.sendApplyNotifications()
+            advanceUntilIdle()
+
+            assertEquals("changed", nodes[0].property, "node 0 recomposition value")
+            assertEquals("changed", nodes[1].property, "node 1 recomposition value")
+            assertEquals(2, nodes[0].changeCount, "node 0 recomposition changeCount")
+            assertEquals(1, nodes[1].changeCount, "node 1 recomposition changeCount")
+        }
+    }
 }
 
 var stateA by mutableStateOf(1000)
diff --git a/compose/test-utils/lint-baseline.xml b/compose/test-utils/lint-baseline.xml
index 42a176b..fd93d9f 100644
--- a/compose/test-utils/lint-baseline.xml
+++ b/compose/test-utils/lint-baseline.xml
@@ -1,4 +1,92 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
 
+    <issue
+        id="BanTargetApiAnnotation"
+        message="Uses @TargetApi annotation"
+        errorLine1="@TargetApi(Build.VERSION_CODES.Q)"
+        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt"
+            line="345"
+            column="1"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 28; however, the containing class androidx.compose.testutils.AndroidComposeTestCaseRunner is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="            bitmap = Bitmap.createBitmap(picture)"
+        errorLine2="                            ~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt"
+            line="274"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.testutils.RenderNodeCapture is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="    private val renderNode = RenderNode(&quot;Test&quot;)"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt"
+            line="347"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.testutils.RenderNodeCapture is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="        renderNode.setPosition(0, 0, width, height)"
+        errorLine2="                   ~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt"
+            line="350"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.testutils.RenderNodeCapture is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="        return renderNode.beginRecording()"
+        errorLine2="                          ~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt"
+            line="351"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.testutils.RenderNodeCapture is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="        renderNode.endRecording()"
+        errorLine2="                   ~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt"
+            line="355"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.testutils.ViewCapture_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="            decorView.viewTreeObserver.registerFrameCommitCallback {"
+        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/ViewCapture.android.kt"
+            line="68"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 26; however, the containing class androidx.compose.testutils.ViewCapture_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="    PixelCopy.request(windowToCapture, boundsInWindow, destBitmap, onCopyFinished, handler)"
+        errorLine2="              ~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/testutils/ViewCapture.android.kt"
+            line="104"
+            column="15"/>
+    </issue>
+
 </issues>
diff --git a/compose/ui/ui-graphics/lint-baseline.xml b/compose/ui/ui-graphics/lint-baseline.xml
index 42a176b..bf0a68e 100644
--- a/compose/ui/ui-graphics/lint-baseline.xml
+++ b/compose/ui/ui-graphics/lint-baseline.xml
@@ -1,4 +1,37 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
 
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.ui.graphics.AndroidColorFilter_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="        BlendModeColorFilter(color.toArgb(), blendMode.toAndroidBlendMode())"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorFilter.android.kt"
+            line="39"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.ui.graphics.CanvasUtils is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="                canvas.enableZ()"
+        errorLine2="                       ~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt"
+            line="39"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.ui.graphics.CanvasUtils is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="                canvas.disableZ()"
+        errorLine2="                       ~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt"
+            line="41"
+            column="24"/>
+    </issue>
+
 </issues>
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
index edcae42..6580236 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
@@ -20,6 +20,7 @@
 import android.view.ViewGroup
 import android.view.inspector.WindowInspector
 import android.widget.TextView
+import androidx.compose.animation.Crossfade
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
@@ -45,6 +46,9 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.InternalComposeApi
 import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.tooling.CompositionData
 import androidx.compose.runtime.tooling.LocalInspectionTables
 import androidx.compose.ui.Alignment
@@ -89,7 +93,7 @@
 import java.util.WeakHashMap
 import kotlin.math.roundToInt
 
-private const val DEBUG = false
+private const val DEBUG = true
 private const val ROOT_ID = 3L
 private const val MAX_RECURSIONS = 2
 private const val MAX_ITERABLE_SIZE = 5
@@ -706,6 +710,43 @@
         }
     }
 
+    @Composable
+    fun First() {
+        Text("First")
+    }
+
+    @Composable
+    fun Second() {
+        Text("Second")
+    }
+
+    @Test
+    fun testCrossfade() {
+        val slotTableRecord = CompositionDataRecord.create()
+
+        show {
+            Inspectable(slotTableRecord) {
+                val showFirst by remember { mutableStateOf(true) }
+                Crossfade(showFirst) {
+                    when (it) {
+                        true -> First()
+                        false -> Second()
+                    }
+                }
+            }
+        }
+        val androidComposeView = findAndroidComposeView()
+        androidComposeView.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
+        val builder = LayoutInspectorTree()
+        builder.hideSystemNodes = false
+        val first = builder.convert(androidComposeView)
+            .flatMap { flatten(it) }
+            .first { it.name == "First" }
+        val hash = packageNameHash(this.javaClass.name.substringBeforeLast('.'))
+        assertThat(first.fileName).isEqualTo("LayoutInspectorTreeTest.kt")
+        assertThat(first.packageHash).isEqualTo(hash)
+    }
+
     @Suppress("SameParameterValue")
     private fun validate(
         result: List<InspectorNode>,
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
index a6779e4..2dca1dd 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.inspection.inspector
 
 import android.view.View
+import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.InternalComposeApi
 import androidx.compose.runtime.tooling.CompositionData
 import androidx.compose.ui.ExperimentalComposeUiApi
@@ -74,7 +75,8 @@
     "ProvideCommonCompositionLocals",
 )
 
-private fun packageNameHash(packageName: String) =
+@VisibleForTesting
+fun packageNameHash(packageName: String) =
     packageName.fold(0) { hash, char -> hash * 31 + char.code }.absoluteValue
 
 /**
diff --git a/compose/ui/ui-test/lint-baseline.xml b/compose/ui/ui-test/lint-baseline.xml
index 42a176b..a9d6c54 100644
--- a/compose/ui/ui-test/lint-baseline.xml
+++ b/compose/ui/ui-test/lint-baseline.xml
@@ -1,4 +1,26 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
 
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.ui.test.android.WindowCapture_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="            decorView.viewTreeObserver.registerFrameCommitCallback {"
+        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/test/android/WindowCapture.android.kt"
+            line="64"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 26; however, the containing class androidx.compose.ui.test.android.WindowCapture_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="    PixelCopy.request(windowToCapture, captureRectInWindow, destBitmap, onCopyFinished, handler)"
+        errorLine2="              ~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/test/android/WindowCapture.android.kt"
+            line="100"
+            column="15"/>
+    </issue>
+
 </issues>
diff --git a/compose/ui/ui-text/api/1.0.0-beta08.txt b/compose/ui/ui-text/api/1.0.0-beta08.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/1.0.0-beta08.txt
+++ b/compose/ui/ui-text/api/1.0.0-beta08.txt
@@ -222,24 +222,43 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Placeholder {
-    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
     method public operator boolean equals(Object? other);
     method public long getHeight-XSAIIZE();
-    method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+    method public int getPlaceholderVerticalAlign-J6kI3mc();
     method public long getWidth-XSAIIZE();
     property public final long height;
-    property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+    property public final int placeholderVerticalAlign;
     property public final long width;
   }
 
-  public enum PlaceholderVerticalAlign {
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+  public final inline class PlaceholderVerticalAlign {
+    ctor public PlaceholderVerticalAlign();
+    method public static int constructor-impl(int value);
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+  }
+
+  public static final class PlaceholderVerticalAlign.Companion {
+    method public int getAboveBaseline-J6kI3mc();
+    method public int getBottom-J6kI3mc();
+    method public int getCenter-J6kI3mc();
+    method public int getTextBottom-J6kI3mc();
+    method public int getTextCenter-J6kI3mc();
+    method public int getTextTop-J6kI3mc();
+    method public int getTop-J6kI3mc();
+    property public final int AboveBaseline;
+    property public final int Bottom;
+    property public final int Center;
+    property public final int TextBottom;
+    property public final int TextCenter;
+    property public final int TextTop;
+    property public final int Top;
   }
 
   public final class SaversKt {
diff --git a/compose/ui/ui-text/api/current.ignore b/compose/ui/ui-text/api/current.ignore
index 5bb1213..221b006 100644
--- a/compose/ui/ui-text/api/current.ignore
+++ b/compose/ui/ui-text/api/current.ignore
@@ -3,6 +3,8 @@
     Added method androidx.compose.ui.text.input.InputEventCallback.onImeAction-KlQnJC8(int)
 
 
+ChangedSuperclass: androidx.compose.ui.text.PlaceholderVerticalAlign:
+    Class androidx.compose.ui.text.PlaceholderVerticalAlign superclass changed from java.lang.Enum to java.lang.Object
 ChangedSuperclass: androidx.compose.ui.text.input.ImeAction:
     Class androidx.compose.ui.text.input.ImeAction superclass changed from java.lang.Enum to java.lang.Object
 ChangedSuperclass: androidx.compose.ui.text.input.KeyboardType:
@@ -13,6 +15,8 @@
     Attempted to remove @NonNull annotation from Field ImeOptions.imeAction
 InvalidNullConversion: Field ImeOptions.keyboardType:
     Attempted to remove @NonNull annotation from Field ImeOptions.keyboardType
+InvalidNullConversion: Field Placeholder.placeholderVerticalAlign:
+    Attempted to remove @NonNull annotation from Field Placeholder.placeholderVerticalAlign
 
 
 RemovedClass: androidx.compose.ui.text.JvmCharHelpers_jvmKt:
@@ -23,6 +27,20 @@
     Removed deprecated method androidx.compose.ui.text.input.InputEventCallback.onImeAction(androidx.compose.ui.text.input.ImeAction)
 
 
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#AboveBaseline:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.AboveBaseline
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Bottom:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Bottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Center:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Center
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextBottom:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextBottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextCenter:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextCenter
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextTop:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextTop
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Top:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Top
 RemovedField: androidx.compose.ui.text.input.ImeAction#Default:
     Removed enum constant androidx.compose.ui.text.input.ImeAction.Default
 RemovedField: androidx.compose.ui.text.input.ImeAction#Done:
@@ -57,6 +75,10 @@
     Removed enum constant androidx.compose.ui.text.input.KeyboardType.Uri
 
 
+RemovedMethod: androidx.compose.ui.text.Placeholder#copy-KJSDsNM(long, long, androidx.compose.ui.text.PlaceholderVerticalAlign):
+    Removed method androidx.compose.ui.text.Placeholder.copy-KJSDsNM(long,long,androidx.compose.ui.text.PlaceholderVerticalAlign)
+RemovedMethod: androidx.compose.ui.text.Placeholder#getPlaceholderVerticalAlign():
+    Removed method androidx.compose.ui.text.Placeholder.getPlaceholderVerticalAlign()
 RemovedMethod: androidx.compose.ui.text.input.ImeOptions#ImeOptions(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
     Removed constructor androidx.compose.ui.text.input.ImeOptions(boolean,androidx.compose.ui.text.input.KeyboardCapitalization,boolean,androidx.compose.ui.text.input.KeyboardType,androidx.compose.ui.text.input.ImeAction)
 RemovedMethod: androidx.compose.ui.text.input.ImeOptions#copy(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -222,24 +222,43 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Placeholder {
-    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
     method public operator boolean equals(Object? other);
     method public long getHeight-XSAIIZE();
-    method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+    method public int getPlaceholderVerticalAlign-J6kI3mc();
     method public long getWidth-XSAIIZE();
     property public final long height;
-    property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+    property public final int placeholderVerticalAlign;
     property public final long width;
   }
 
-  public enum PlaceholderVerticalAlign {
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+  public final inline class PlaceholderVerticalAlign {
+    ctor public PlaceholderVerticalAlign();
+    method public static int constructor-impl(int value);
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+  }
+
+  public static final class PlaceholderVerticalAlign.Companion {
+    method public int getAboveBaseline-J6kI3mc();
+    method public int getBottom-J6kI3mc();
+    method public int getCenter-J6kI3mc();
+    method public int getTextBottom-J6kI3mc();
+    method public int getTextCenter-J6kI3mc();
+    method public int getTextTop-J6kI3mc();
+    method public int getTop-J6kI3mc();
+    property public final int AboveBaseline;
+    property public final int Bottom;
+    property public final int Center;
+    property public final int TextBottom;
+    property public final int TextCenter;
+    property public final int TextTop;
+    property public final int Top;
   }
 
   public final class SaversKt {
diff --git a/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt b/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt
index 2f0a4c8..7b15f2b 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt
@@ -231,24 +231,43 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Placeholder {
-    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
     method public operator boolean equals(Object? other);
     method public long getHeight-XSAIIZE();
-    method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+    method public int getPlaceholderVerticalAlign-J6kI3mc();
     method public long getWidth-XSAIIZE();
     property public final long height;
-    property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+    property public final int placeholderVerticalAlign;
     property public final long width;
   }
 
-  public enum PlaceholderVerticalAlign {
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+  public final inline class PlaceholderVerticalAlign {
+    ctor public PlaceholderVerticalAlign();
+    method public static int constructor-impl(int value);
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+  }
+
+  public static final class PlaceholderVerticalAlign.Companion {
+    method public int getAboveBaseline-J6kI3mc();
+    method public int getBottom-J6kI3mc();
+    method public int getCenter-J6kI3mc();
+    method public int getTextBottom-J6kI3mc();
+    method public int getTextCenter-J6kI3mc();
+    method public int getTextTop-J6kI3mc();
+    method public int getTop-J6kI3mc();
+    property public final int AboveBaseline;
+    property public final int Bottom;
+    property public final int Center;
+    property public final int TextBottom;
+    property public final int TextCenter;
+    property public final int TextTop;
+    property public final int Top;
   }
 
   public final class SaversKt {
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index 2f0a4c8..7b15f2b 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -231,24 +231,43 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Placeholder {
-    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
     method public operator boolean equals(Object? other);
     method public long getHeight-XSAIIZE();
-    method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+    method public int getPlaceholderVerticalAlign-J6kI3mc();
     method public long getWidth-XSAIIZE();
     property public final long height;
-    property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+    property public final int placeholderVerticalAlign;
     property public final long width;
   }
 
-  public enum PlaceholderVerticalAlign {
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+  public final inline class PlaceholderVerticalAlign {
+    ctor public PlaceholderVerticalAlign();
+    method public static int constructor-impl(int value);
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+  }
+
+  public static final class PlaceholderVerticalAlign.Companion {
+    method public int getAboveBaseline-J6kI3mc();
+    method public int getBottom-J6kI3mc();
+    method public int getCenter-J6kI3mc();
+    method public int getTextBottom-J6kI3mc();
+    method public int getTextCenter-J6kI3mc();
+    method public int getTextTop-J6kI3mc();
+    method public int getTop-J6kI3mc();
+    property public final int AboveBaseline;
+    property public final int Bottom;
+    property public final int Center;
+    property public final int TextBottom;
+    property public final int TextCenter;
+    property public final int TextTop;
+    property public final int Top;
   }
 
   public final class SaversKt {
diff --git a/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt b/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt
+++ b/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt
@@ -222,24 +222,43 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Placeholder {
-    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
     method public operator boolean equals(Object? other);
     method public long getHeight-XSAIIZE();
-    method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+    method public int getPlaceholderVerticalAlign-J6kI3mc();
     method public long getWidth-XSAIIZE();
     property public final long height;
-    property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+    property public final int placeholderVerticalAlign;
     property public final long width;
   }
 
-  public enum PlaceholderVerticalAlign {
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+  public final inline class PlaceholderVerticalAlign {
+    ctor public PlaceholderVerticalAlign();
+    method public static int constructor-impl(int value);
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+  }
+
+  public static final class PlaceholderVerticalAlign.Companion {
+    method public int getAboveBaseline-J6kI3mc();
+    method public int getBottom-J6kI3mc();
+    method public int getCenter-J6kI3mc();
+    method public int getTextBottom-J6kI3mc();
+    method public int getTextCenter-J6kI3mc();
+    method public int getTextTop-J6kI3mc();
+    method public int getTop-J6kI3mc();
+    property public final int AboveBaseline;
+    property public final int Bottom;
+    property public final int Center;
+    property public final int TextBottom;
+    property public final int TextCenter;
+    property public final int TextTop;
+    property public final int Top;
   }
 
   public final class SaversKt {
diff --git a/compose/ui/ui-text/api/restricted_current.ignore b/compose/ui/ui-text/api/restricted_current.ignore
index 5bb1213..221b006 100644
--- a/compose/ui/ui-text/api/restricted_current.ignore
+++ b/compose/ui/ui-text/api/restricted_current.ignore
@@ -3,6 +3,8 @@
     Added method androidx.compose.ui.text.input.InputEventCallback.onImeAction-KlQnJC8(int)
 
 
+ChangedSuperclass: androidx.compose.ui.text.PlaceholderVerticalAlign:
+    Class androidx.compose.ui.text.PlaceholderVerticalAlign superclass changed from java.lang.Enum to java.lang.Object
 ChangedSuperclass: androidx.compose.ui.text.input.ImeAction:
     Class androidx.compose.ui.text.input.ImeAction superclass changed from java.lang.Enum to java.lang.Object
 ChangedSuperclass: androidx.compose.ui.text.input.KeyboardType:
@@ -13,6 +15,8 @@
     Attempted to remove @NonNull annotation from Field ImeOptions.imeAction
 InvalidNullConversion: Field ImeOptions.keyboardType:
     Attempted to remove @NonNull annotation from Field ImeOptions.keyboardType
+InvalidNullConversion: Field Placeholder.placeholderVerticalAlign:
+    Attempted to remove @NonNull annotation from Field Placeholder.placeholderVerticalAlign
 
 
 RemovedClass: androidx.compose.ui.text.JvmCharHelpers_jvmKt:
@@ -23,6 +27,20 @@
     Removed deprecated method androidx.compose.ui.text.input.InputEventCallback.onImeAction(androidx.compose.ui.text.input.ImeAction)
 
 
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#AboveBaseline:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.AboveBaseline
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Bottom:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Bottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Center:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Center
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextBottom:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextBottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextCenter:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextCenter
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextTop:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextTop
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Top:
+    Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Top
 RemovedField: androidx.compose.ui.text.input.ImeAction#Default:
     Removed enum constant androidx.compose.ui.text.input.ImeAction.Default
 RemovedField: androidx.compose.ui.text.input.ImeAction#Done:
@@ -57,6 +75,10 @@
     Removed enum constant androidx.compose.ui.text.input.KeyboardType.Uri
 
 
+RemovedMethod: androidx.compose.ui.text.Placeholder#copy-KJSDsNM(long, long, androidx.compose.ui.text.PlaceholderVerticalAlign):
+    Removed method androidx.compose.ui.text.Placeholder.copy-KJSDsNM(long,long,androidx.compose.ui.text.PlaceholderVerticalAlign)
+RemovedMethod: androidx.compose.ui.text.Placeholder#getPlaceholderVerticalAlign():
+    Removed method androidx.compose.ui.text.Placeholder.getPlaceholderVerticalAlign()
 RemovedMethod: androidx.compose.ui.text.input.ImeOptions#ImeOptions(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
     Removed constructor androidx.compose.ui.text.input.ImeOptions(boolean,androidx.compose.ui.text.input.KeyboardCapitalization,boolean,androidx.compose.ui.text.input.KeyboardType,androidx.compose.ui.text.input.ImeAction)
 RemovedMethod: androidx.compose.ui.text.input.ImeOptions#copy(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -222,24 +222,43 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Placeholder {
-    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+    method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
     method public operator boolean equals(Object? other);
     method public long getHeight-XSAIIZE();
-    method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+    method public int getPlaceholderVerticalAlign-J6kI3mc();
     method public long getWidth-XSAIIZE();
     property public final long height;
-    property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+    property public final int placeholderVerticalAlign;
     property public final long width;
   }
 
-  public enum PlaceholderVerticalAlign {
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
-    enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+  public final inline class PlaceholderVerticalAlign {
+    ctor public PlaceholderVerticalAlign();
+    method public static int constructor-impl(int value);
+    method public static inline boolean equals-impl(int p, Object? p1);
+    method public static boolean equals-impl0(int p1, int p2);
+    method public int getValue();
+    method public static inline int hashCode-impl(int p);
+    method public static String toString-impl(int $this);
+    property public final int value;
+    field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+  }
+
+  public static final class PlaceholderVerticalAlign.Companion {
+    method public int getAboveBaseline-J6kI3mc();
+    method public int getBottom-J6kI3mc();
+    method public int getCenter-J6kI3mc();
+    method public int getTextBottom-J6kI3mc();
+    method public int getTextCenter-J6kI3mc();
+    method public int getTextTop-J6kI3mc();
+    method public int getTop-J6kI3mc();
+    property public final int AboveBaseline;
+    property public final int Bottom;
+    property public final int Center;
+    property public final int TextBottom;
+    property public final int TextCenter;
+    property public final int TextTop;
+    property public final int Top;
   }
 
   public final class SaversKt {
diff --git a/compose/ui/ui-text/benchmark/lint-baseline.xml b/compose/ui/ui-text/benchmark/lint-baseline.xml
index 42a176b..cab51d3 100644
--- a/compose/ui/ui-text/benchmark/lint-baseline.xml
+++ b/compose/ui/ui-text/benchmark/lint-baseline.xml
@@ -1,4 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
 
+    <issue
+        id="BanUncheckedReflection"
+        message="Calling Method.invoke without an SDK check"
+        errorLine1="            freeCaches.invoke(null)"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/compose/ui/text/benchmark/TextBenchmarkTestRule.kt"
+            line="81"
+            column="13"/>
+    </issue>
+
 </issues>
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt
index ee998a9..94d26db 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt
@@ -84,4 +84,5 @@
         PlaceholderVerticalAlign.TextTop -> PlaceholderSpan.ALIGN_TEXT_TOP
         PlaceholderVerticalAlign.TextBottom -> PlaceholderSpan.ALIGN_TEXT_BOTTOM
         PlaceholderVerticalAlign.TextCenter -> PlaceholderSpan.ALIGN_TEXT_CENTER
+        else -> error("Invalid PlaceholderVerticalAlign")
     }
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
index cf6b935..eca4d3d 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
@@ -84,34 +84,51 @@
  * The settings used to specify how a placeholder is vertically aligned within a text line.
  * @see Placeholder
  */
-enum class PlaceholderVerticalAlign {
-    /** Align the bottom of the placeholder with the baseline. */
-    AboveBaseline,
-    /** Align the top of the placeholder with the top of the entire line. */
-    Top,
-    /** Align the bottom of the placeholder with the bottom of the entire line. */
-    Bottom,
-    /** Align the center of the placeholder with the center of the entire line. */
-    Center,
-    /**
-     *  Align the top of the placeholder with the top of the proceeding text.
-     *  It is different from the [Top] when there are texts with different font size, font or other
-     *  styles in the same line. This option will use the proceeding text's top instead of the
-     *  whole line's top.
-     */
-    TextTop,
-    /**
-     * Align the bottom of the placeholder with the bottom of the proceeding text.
-     * It is different from the [TextBottom] when there are texts with different font size, font or
-     * other styles in the same line. This option will use the proceeding text's bottom instead of
-     * the whole line's bottom.
-     */
-    TextBottom,
-    /**
-     * Align the center of the placeholder with the center of the proceeding text.
-     * It is different from the [Center] when there are texts with different font size, font or
-     * other styles in the same line. This option will use the proceeding text's center instead of
-     * the whole line's center.
-     */
-    TextCenter,
+@Suppress("INLINE_CLASS_DEPRECATED")
+inline class PlaceholderVerticalAlign(val value: Int) {
+
+    override fun toString(): String {
+        return when (this) {
+            AboveBaseline -> "AboveBaseline"
+            Top -> "Top"
+            Bottom -> "Bottom"
+            Center -> "Center"
+            TextTop -> "TextTop"
+            TextBottom -> "TextBottom"
+            TextCenter -> "TextCenter"
+            else -> "Invalid"
+        }
+    }
+
+    companion object {
+        /** Align the bottom of the placeholder with the baseline. */
+        val AboveBaseline = PlaceholderVerticalAlign(1)
+        /** Align the top of the placeholder with the top of the entire line. */
+        val Top = PlaceholderVerticalAlign(2)
+        /** Align the bottom of the placeholder with the bottom of the entire line. */
+        val Bottom = PlaceholderVerticalAlign(3)
+        /** Align the center of the placeholder with the center of the entire line. */
+        val Center = PlaceholderVerticalAlign(4)
+        /**
+         *  Align the top of the placeholder with the top of the proceeding text.
+         *  It is different from the [Top] when there are texts with different font size, font or other
+         *  styles in the same line. This option will use the proceeding text's top instead of the
+         *  whole line's top.
+         */
+        val TextTop = PlaceholderVerticalAlign(5)
+        /**
+         * Align the bottom of the placeholder with the bottom of the proceeding text.
+         * It is different from the [TextBottom] when there are texts with different font size, font or
+         * other styles in the same line. This option will use the proceeding text's bottom instead of
+         * the whole line's bottom.
+         */
+        val TextBottom = PlaceholderVerticalAlign(6)
+        /**
+         * Align the center of the placeholder with the center of the proceeding text.
+         * It is different from the [Center] when there are texts with different font size, font or
+         * other styles in the same line. This option will use the proceeding text's center instead of
+         * the whole line's center.
+         */
+        val TextCenter = PlaceholderVerticalAlign(7)
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt
index 5934cd7..9105a83 100644
--- a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt
+++ b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt
@@ -866,6 +866,7 @@
         PlaceholderVerticalAlign.Top -> PlaceholderAlignment.TOP
         PlaceholderVerticalAlign.Bottom -> PlaceholderAlignment.BOTTOM
         PlaceholderVerticalAlign.Center -> PlaceholderAlignment.MIDDLE
+        else -> error("Invalid PlaceholderVerticalAlign.")
     }
 }
 
diff --git a/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt b/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt
index 0817e3f..9a00966 100644
--- a/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt
+++ b/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt
@@ -432,7 +432,7 @@
     return SourceInformationContext(
         name = name,
         sourceFile = sourceFile ?: parent?.sourceFile,
-        packageHash = packageHash,
+        packageHash = if (sourceFile != null) packageHash else parent?.packageHash ?: packageHash,
         locations = sourceLocations,
         repeatOffset = repeatOffset,
         parameters = parameters,
diff --git a/compose/ui/ui-tooling/lint-baseline.xml b/compose/ui/ui-tooling/lint-baseline.xml
index 42a176b..9d72f7c 100644
--- a/compose/ui/ui-tooling/lint-baseline.xml
+++ b/compose/ui/ui-tooling/lint-baseline.xml
@@ -1,4 +1,37 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
 
+    <issue
+        id="BanUncheckedReflection"
+        message="Calling Method.invoke without an SDK check"
+        errorLine1="                val result = designInfoMethod.invoke("
+        errorLine2="                             ^">
+        <location
+            file="src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt"
+            line="384"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="BanUncheckedReflection"
+        message="Calling Method.invoke without an SDK check"
+        errorLine1="    return invoke(instance, *arguments)"
+        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/compose/ui/tooling/preview/PreviewUtils.kt"
+            line="141"
+            column="12"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 26; however, the containing class androidx.compose.ui.tooling.preview.LayoutlibFontResourceLoader is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="            is ResourceFont -> context.resources.getFont(font.resId)"
+        errorLine2="                                                 ~~~~~~~">
+        <location
+            file="src/main/java/androidx/compose/ui/tooling/preview/LayoutlibFontResourceLoader.kt"
+            line="33"
+            column="50"/>
+    </issue>
+
 </issues>
diff --git a/compose/ui/ui/api/1.0.0-beta08.txt b/compose/ui/ui/api/1.0.0-beta08.txt
index ef6a13d..fa7c70b 100644
--- a/compose/ui/ui/api/1.0.0-beta08.txt
+++ b/compose/ui/ui/api/1.0.0-beta08.txt
@@ -276,7 +276,7 @@
   }
 
   public interface FocusManager {
-    method public void clearFocus(optional boolean forcedClear);
+    method public void clearFocus(optional boolean force);
     method public boolean moveFocus-3ESFkO8(int focusDirection);
   }
 
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
index f30e81b..209860c 100644
--- a/compose/ui/ui/api/current.ignore
+++ b/compose/ui/ui/api/current.ignore
@@ -53,6 +53,8 @@
     Attempted to remove @NonNull annotation from method androidx.compose.ui.semantics.SemanticsPropertiesKt.getImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver)
 
 
+ParameterNameChange: androidx.compose.ui.focus.FocusManager#clearFocus(boolean) parameter #0:
+    Attempted to change parameter name from forcedClear to force in method androidx.compose.ui.focus.FocusManager.clearFocus
 ParameterNameChange: androidx.compose.ui.semantics.SemanticsPropertiesKt#setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String) parameter #1:
     Attempted to change parameter name from p to value in method androidx.compose.ui.semantics.SemanticsPropertiesKt.setContentDescription
 ParameterNameChange: androidx.compose.ui.semantics.SemanticsPropertiesKt#setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString) parameter #1:
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index ef6a13d..fa7c70b 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -276,7 +276,7 @@
   }
 
   public interface FocusManager {
-    method public void clearFocus(optional boolean forcedClear);
+    method public void clearFocus(optional boolean force);
     method public boolean moveFocus-3ESFkO8(int focusDirection);
   }
 
diff --git a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
index e989440..22062ae 100644
--- a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
@@ -347,7 +347,7 @@
   }
 
   public interface FocusManager {
-    method public void clearFocus(optional boolean forcedClear);
+    method public void clearFocus(optional boolean force);
     method public boolean moveFocus-3ESFkO8(int focusDirection);
     method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public default boolean moveFocusIn();
     method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public default boolean moveFocusOut();
@@ -1849,7 +1849,7 @@
 
   @androidx.compose.ui.ExperimentalComposeUiApi public final class RelocationRequester {
     ctor public RelocationRequester();
-    method public void bringIntoParentBounds();
+    method public void bringIntoView();
   }
 
   public final class RelocationRequesterModifierKt {
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index e989440..22062ae 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -347,7 +347,7 @@
   }
 
   public interface FocusManager {
-    method public void clearFocus(optional boolean forcedClear);
+    method public void clearFocus(optional boolean force);
     method public boolean moveFocus-3ESFkO8(int focusDirection);
     method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public default boolean moveFocusIn();
     method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public default boolean moveFocusOut();
@@ -1849,7 +1849,7 @@
 
   @androidx.compose.ui.ExperimentalComposeUiApi public final class RelocationRequester {
     ctor public RelocationRequester();
-    method public void bringIntoParentBounds();
+    method public void bringIntoView();
   }
 
   public final class RelocationRequesterModifierKt {
diff --git a/compose/ui/ui/api/restricted_1.0.0-beta08.txt b/compose/ui/ui/api/restricted_1.0.0-beta08.txt
index 7938cd4..60cf61c 100644
--- a/compose/ui/ui/api/restricted_1.0.0-beta08.txt
+++ b/compose/ui/ui/api/restricted_1.0.0-beta08.txt
@@ -276,7 +276,7 @@
   }
 
   public interface FocusManager {
-    method public void clearFocus(optional boolean forcedClear);
+    method public void clearFocus(optional boolean force);
     method public boolean moveFocus-3ESFkO8(int focusDirection);
   }
 
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
index f30e81b..209860c 100644
--- a/compose/ui/ui/api/restricted_current.ignore
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -53,6 +53,8 @@
     Attempted to remove @NonNull annotation from method androidx.compose.ui.semantics.SemanticsPropertiesKt.getImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver)
 
 
+ParameterNameChange: androidx.compose.ui.focus.FocusManager#clearFocus(boolean) parameter #0:
+    Attempted to change parameter name from forcedClear to force in method androidx.compose.ui.focus.FocusManager.clearFocus
 ParameterNameChange: androidx.compose.ui.semantics.SemanticsPropertiesKt#setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String) parameter #1:
     Attempted to change parameter name from p to value in method androidx.compose.ui.semantics.SemanticsPropertiesKt.setContentDescription
 ParameterNameChange: androidx.compose.ui.semantics.SemanticsPropertiesKt#setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString) parameter #1:
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 7938cd4..60cf61c 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -276,7 +276,7 @@
   }
 
   public interface FocusManager {
-    method public void clearFocus(optional boolean forcedClear);
+    method public void clearFocus(optional boolean force);
     method public boolean moveFocus-3ESFkO8(int focusDirection);
   }
 
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
index 16d5c82..7914fe1 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
@@ -47,8 +47,9 @@
 import androidx.compose.ui.demos.gestures.ScaleGestureFilterDemo
 import androidx.compose.ui.demos.gestures.ScrollGestureFilterDemo
 import androidx.compose.ui.demos.gestures.VerticalScrollerInDrawerDemo
-import androidx.compose.ui.demos.input.nestedscroll.BringIntoParentBoundsDemo
+import androidx.compose.ui.demos.input.nestedscroll.BringIntoViewDemo
 import androidx.compose.ui.demos.keyinput.KeyInputDemo
+import androidx.compose.ui.demos.scroll.RequestRectangleOnScreenDemo
 import androidx.compose.ui.demos.viewinterop.ViewInteropDemo
 import androidx.compose.ui.samples.NestedScrollConnectionSample
 
@@ -128,7 +129,8 @@
 private val RelocationDemos = DemoCategory(
     "Relocation",
     listOf(
-        ComposableDemo("Bring Item Into View") { BringIntoParentBoundsDemo() }
+        ComposableDemo("Bring Into View") { BringIntoViewDemo() },
+        ComposableDemo("Request Rectangle On Screen") { RequestRectangleOnScreenDemo() }
     )
 )
 
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoParentBoundsDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoViewDemo.kt
similarity index 93%
rename from compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoParentBoundsDemo.kt
rename to compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoViewDemo.kt
index bc44376..d86d786 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoParentBoundsDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoViewDemo.kt
@@ -42,7 +42,7 @@
 
 @OptIn(ExperimentalComposeUiApi::class)
 @Composable
-fun BringIntoParentBoundsDemo() {
+fun BringIntoViewDemo() {
     val greenRequester = remember { RelocationRequester() }
     val redRequester = remember { RelocationRequester() }
     Column {
@@ -54,10 +54,10 @@
             Box(Modifier.background(Red).size(100.dp).relocationRequester(redRequester))
             Box(Modifier.background(LightGray).size(100.dp))
         }
-        Button(onClick = { greenRequester.bringIntoParentBounds() }) {
+        Button(onClick = { greenRequester.bringIntoView() }) {
             Text("Bring Green box into view")
         }
-        Button(onClick = { redRequester.bringIntoParentBounds() }) {
+        Button(onClick = { redRequester.bringIntoView() }) {
             Text("Bring Red box into view")
         }
     }
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/RequestRectangleOnScreeenDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/RequestRectangleOnScreeenDemo.kt
new file mode 100644
index 0000000..c94d797
--- /dev/null
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/RequestRectangleOnScreeenDemo.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 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.compose.ui.demos.scroll
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.RelocationRequester
+import androidx.compose.ui.layout.relocationRequester
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun RequestRectangleOnScreenDemo() {
+    val relocationRequester = remember { RelocationRequester() }
+    Column {
+        TextField(value = "Click here to bring up the soft keyboard", onValueChange = {})
+        Button(onClick = { relocationRequester.bringIntoView() }) {
+            Text("Bring blue rectangle into view")
+        }
+        Spacer(Modifier.weight(weight = 1f, fill = true))
+        Box(
+            Modifier
+                .size(50.dp)
+                .background(Color.Blue)
+                .relocationRequester(relocationRequester)
+        )
+    }
+}
diff --git a/compose/ui/ui/lint-baseline.xml b/compose/ui/ui/lint-baseline.xml
index b75d649..b2f4c19 100644
--- a/compose/ui/ui/lint-baseline.xml
+++ b/compose/ui/ui/lint-baseline.xml
@@ -12,4 +12,70 @@
             column="63"/>
     </issue>
 
+    <issue
+        id="BanUncheckedReflection"
+        message="Calling Method.invoke without an SDK check"
+        errorLine1="                updateDisplayListIfDirtyMethod?.invoke(view)"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt"
+            line="337"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 26; however, the containing class androidx.compose.ui.autofill.AndroidAutofillDebugUtils_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="    autofillManager.registerCallback(AutofillCallback)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillDebugUtils.android.kt"
+            line="67"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 26; however, the containing class androidx.compose.ui.autofill.AndroidAutofillDebugUtils_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="    autofillManager.unregisterCallback(AutofillCallback)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillDebugUtils.android.kt"
+            line="77"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 23; however, the containing class androidx.compose.ui.res.ColorResources_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="        Color(context.resources.getColor(id, context.theme))"
+        errorLine2="                                ~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/res/ColorResources.android.kt"
+            line="37"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.ui.input.pointer.MotionEventAdapter_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="    return Offset(getRawX(index), getRawY(index))"
+        errorLine2="                  ~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt"
+            line="194"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="ClassVerificationFailure"
+        message="This call references a method added in API level 29; however, the containing class androidx.compose.ui.input.pointer.MotionEventAdapter_androidKt is reachable from earlier API levels and will fail run-time class verification."
+        errorLine1="    return Offset(getRawX(index), getRawY(index))"
+        errorLine2="                                  ~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt"
+            line="194"
+            column="35"/>
+    </issue>
+
 </issues>
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/RelocationSamples.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/RelocationSamples.kt
index dc4f837..f1e168a 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/RelocationSamples.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/RelocationSamples.kt
@@ -36,14 +36,14 @@
 @ExperimentalComposeUiApi
 @Sampled
 @Composable
-fun BringIntoParentBoundsSample() {
+fun BringIntoViewSample() {
     val relocationRequester = remember { RelocationRequester() }
     Column {
         Box(Modifier.width(100.dp).horizontalScroll(rememberScrollState())) {
             Box(Modifier.size(100.dp))
             Box(Modifier.size(100.dp).relocationRequester(relocationRequester))
         }
-        Button(onClick = { relocationRequester.bringIntoParentBounds() }) {
+        Button(onClick = { relocationRequester.bringIntoView() }) {
             Text("Bring box into view")
         }
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
index 60dfe8c..383b749 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
@@ -305,6 +305,44 @@
         rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Blue }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun testVectorChangeSize() {
+        val size = mutableStateOf(200)
+        val color = mutableStateOf(Color.Magenta)
+
+        rule.setContent {
+            val background = Modifier.background(Color.Red).paint(
+                createTestVectorPainter(size.value, color.value),
+                alignment = Alignment.TopStart
+            )
+            AtLeastSize(size = 400, modifier = background) {
+            }
+        }
+
+        takeScreenShot(400).apply {
+            assertEquals(getPixel(100, 100), Color.Magenta.toArgb())
+            assertEquals(getPixel(300, 300), Color.Red.toArgb())
+        }
+
+        size.value = 400
+        color.value = Color.Cyan
+
+        takeScreenShot(400).apply {
+            assertEquals(getPixel(100, 100), Color.Cyan.toArgb())
+            assertEquals(getPixel(300, 300), Color.Cyan.toArgb())
+        }
+
+        size.value = 50
+        color.value = Color.Yellow
+
+        takeScreenShot(400).apply {
+            assertEquals(getPixel(10, 10), Color.Yellow.toArgb())
+            assertEquals(getPixel(100, 100), Color.Red.toArgb())
+            assertEquals(getPixel(300, 300), Color.Red.toArgb())
+        }
+    }
+
     @Composable
     private fun VectorTint(
         size: Int = 200,
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/EditorInfoTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/EditorInfoTest.kt
index 5894e49..c69138c 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/EditorInfoTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/EditorInfoTest.kt
@@ -25,12 +25,14 @@
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.update
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
 class EditorInfoTest {
 
     @Test
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
index 3e51d7d..f7b1ade 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.input.key.KeyEvent
@@ -3095,6 +3096,10 @@
         TODO("Not yet implemented")
     }
 
+    override fun requestRectangleOnScreen(rect: Rect) {
+        TODO("Not yet implemented")
+    }
+
     override val measureIteration: Long
         get() = 0
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationRequesterModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationRequesterModifierTest.kt
index 680b624..4fd76bd 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationRequesterModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationRequesterModifierTest.kt
@@ -105,7 +105,7 @@
         }
 
         // Act.
-        rule.runOnIdle { relocationRequester.bringIntoParentBounds() }
+        rule.runOnIdle { relocationRequester.bringIntoView() }
 
         // Assert.
         rule.waitForIdle()
@@ -144,7 +144,7 @@
         }
 
         // Act.
-        rule.runOnIdle { relocationRequester.bringIntoParentBounds() }
+        rule.runOnIdle { relocationRequester.bringIntoView() }
 
         // Assert.
         rule.waitForIdle()
@@ -183,7 +183,7 @@
         }
 
         // Act.
-        rule.runOnIdle { relocationRequester.bringIntoParentBounds() }
+        rule.runOnIdle { relocationRequester.bringIntoView() }
 
         // Assert.
         rule.waitForIdle()
@@ -228,7 +228,7 @@
         }
 
         // Act.
-        rule.runOnIdle { relocationRequester.bringIntoParentBounds() }
+        rule.runOnIdle { relocationRequester.bringIntoView() }
 
         // Assert.
         rule.waitForIdle()
@@ -273,7 +273,7 @@
         }
 
         // Act.
-        rule.runOnIdle { relocationRequester.bringIntoParentBounds() }
+        rule.runOnIdle { relocationRequester.bringIntoView() }
 
         // Assert.
         rule.waitForIdle()
@@ -308,7 +308,7 @@
         }
 
         // Act.
-        rule.runOnIdle { relocationRequester.bringIntoParentBounds() }
+        rule.runOnIdle { relocationRequester.bringIntoView() }
 
         // Assert.
         rule.waitForIdle()
@@ -348,7 +348,7 @@
         rule.awaitIdle()
 
         // Act.
-        relocationRequester.bringIntoParentBounds()
+        relocationRequester.bringIntoView()
 
         // Assert.
         rule.awaitIdle()
@@ -388,7 +388,7 @@
         rule.awaitIdle()
 
         // Act.
-        relocationRequester.bringIntoParentBounds()
+        relocationRequester.bringIntoView()
 
         // Assert.
         rule.awaitIdle()
@@ -446,7 +446,7 @@
             rule.awaitIdle()
 
             // Act.
-            relocationRequester.bringIntoParentBounds()
+            relocationRequester.bringIntoView()
 
             // Assert.
             rule.awaitIdle()
@@ -504,7 +504,7 @@
             rule.awaitIdle()
 
             // Act.
-            relocationRequester.bringIntoParentBounds()
+            relocationRequester.bringIntoView()
 
             // Assert.
             rule.awaitIdle()
@@ -557,7 +557,7 @@
             rule.awaitIdle()
 
             // Act.
-            relocationRequester.bringIntoParentBounds()
+            relocationRequester.bringIntoView()
 
             // Assert.
             rule.awaitIdle()
@@ -615,7 +615,7 @@
             rule.awaitIdle()
 
             // Act.
-            relocationRequester.bringIntoParentBounds()
+            relocationRequester.bringIntoView()
 
             // Assert.
             rule.awaitIdle()
@@ -693,7 +693,7 @@
         rule.awaitIdle()
 
         // Act.
-        relocationRequester.bringIntoParentBounds()
+        relocationRequester.bringIntoView()
 
         // Assert.
         rule.awaitIdle()
@@ -757,7 +757,7 @@
             rule.awaitIdle()
 
             // Act.
-            relocationRequester.bringIntoParentBounds()
+            relocationRequester.bringIntoView()
 
             // Assert.
             rule.awaitIdle()
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/ComposeViewOverlayTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/ComposeViewOverlayTest.kt
index 142aa4d..637f93f 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/ComposeViewOverlayTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/ComposeViewOverlayTest.kt
@@ -31,6 +31,7 @@
 import androidx.lifecycle.LifecycleOwner
 import androidx.test.ext.junit.rules.activityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.runBlocking
@@ -57,6 +58,7 @@
      * originally attached to the target window "for real."
      */
     @OptIn(InternalComposeUiApi::class)
+    @LargeTest
     @Test
     fun testComposeViewMovedToOverlay() {
         var factoryCallCount = 0
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index 7a6becc..08d3717 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -62,6 +62,7 @@
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusManagerImpl
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect as ComposeRect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.CanvasHolder
 import androidx.compose.ui.graphics.Matrix
@@ -638,6 +639,10 @@
         }
     }
 
+    override fun requestRectangleOnScreen(rect: ComposeRect) {
+        requestRectangleOnScreen(rect.toRect())
+    }
+
     override fun dispatchDraw(canvas: android.graphics.Canvas) {
         if (!isAttachedToWindow) {
             invalidateLayers(root)
@@ -656,6 +661,18 @@
                 layer.updateDisplayList()
             }
         }
+
+        if (ViewLayer.shouldUseDispatchDraw) {
+            // We must update the display list of all children using dispatchDraw()
+            // instead of updateDisplayList(). But since we don't want to actually draw
+            // the contents, we will clip out everything from the canvas.
+            val saveCount = canvas.save()
+            canvas.clipRect(0f, 0f, 0f, 0f)
+
+            super.dispatchDraw(canvas)
+            canvas.restoreToCount(saveCount)
+        }
+
         dirtyLayers.clear()
         isDrawingContent = false
 
@@ -668,17 +685,6 @@
             dirtyLayers.addAll(postponed)
             postponed.clear()
         }
-
-        if (ViewLayer.shouldUseDispatchDraw) {
-            // We must update the display list of all children using dispatchDraw()
-            // instead of updateDisplayList(). But since we don't want to actually draw
-            // the contents, we will clip out everything from the canvas.
-            val saveCount = canvas.save()
-            canvas.clipRect(0f, 0f, 0f, 0f)
-
-            super.dispatchDraw(canvas)
-            canvas.restoreToCount(saveCount)
-        }
     }
 
     internal fun notifyLayerIsDirty(layer: OwnedLayer, isDirty: Boolean) {
@@ -1167,3 +1173,7 @@
     other[3, 2] = ((-a30 * b03 + a31 * b01 - a32 * b00) * invDet)
     other[3, 3] = ((a20 * b03 - a21 * b01 + a22 * b00) * invDet)
 }
+
+private fun ComposeRect.toRect(): Rect {
+    return Rect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt())
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index f0ae381..e84f2ca 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -69,6 +69,7 @@
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastMap
 import androidx.core.view.AccessibilityDelegateCompat
 import androidx.core.view.ViewCompat
 import androidx.core.view.ViewCompat.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
@@ -2012,7 +2013,7 @@
         }
 
         return node.config.getOrNull(SemanticsProperties.Text)
-            ?.map { it.text }?.fastJoinToString(",")
+            ?.fastMap { it.text }?.fastJoinToString(",")
     }
 
     /**
@@ -2025,7 +2026,7 @@
         val editableText = node.config.getOrNull(SemanticsProperties.EditableText)
         return if (editableText.isNullOrEmpty()) {
             node.config.getOrNull(SemanticsProperties.Text)
-                ?.map { it.text }?.fastJoinToString(",")
+                ?.fastMap { it.text }?.fastJoinToString(",")
         } else {
             editableText.text
         }
@@ -2044,7 +2045,7 @@
         val contentDescription =
             node.unmergedConfig.getOrNull(SemanticsProperties.ContentDescription)
         if (!contentDescription.isNullOrEmpty()) {
-            return contentDescription.joinToString(",")
+            return contentDescription.fastJoinToString(",")
         }
 
         if (node.unmergedConfig.contains(SemanticsProperties.Text) ||
@@ -2075,7 +2076,7 @@
                 val contentDescription =
                     childNode.unmergedConfig.getOrNull(SemanticsProperties.ContentDescription)
                 if (!contentDescription.isNullOrEmpty()) {
-                    childDescriptions.add(contentDescription.joinToString(","))
+                    childDescriptions.add(contentDescription.fastJoinToString(","))
                     return@fastForEach
                 }
 
@@ -2091,7 +2092,7 @@
                 // check if it's a text node
                 val text = childNode.unmergedConfig.getOrNull(SemanticsProperties.Text)
                 if (!text.isNullOrEmpty()) {
-                    childDescriptions.add(text.map { it.text }.fastJoinToString(","))
+                    childDescriptions.add(text.fastMap { it.text }.fastJoinToString(","))
                     return@fastForEach
                 }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
index cb20e73..c78d9ec 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
@@ -45,10 +45,10 @@
      * Call this function to clear focus from the currently focused component, and set the focus to
      * the root focus modifier.
      *
-     *  @param forcedClear: Whether we should forcefully clear focus regardless of whether we have
+     *  @param force: Whether we should forcefully clear focus regardless of whether we have
      *  any components that have Captured focus.
      */
-    fun clearFocus(forcedClear: Boolean = false)
+    fun clearFocus(force: Boolean = false)
 
     /**
      * Moves focus in the specified [direction][FocusDirection].
@@ -157,13 +157,13 @@
     /**
      * Call this function to set the focus to the root focus modifier.
      *
-     * @param forcedClear: Whether we should forcefully clear focus regardless of whether we have
+     * @param force: Whether we should forcefully clear focus regardless of whether we have
      * any components that have captured focus.
      *
      * This could be used to clear focus when a user clicks on empty space outside a focusable
      * component.
      */
-    override fun clearFocus(forcedClear: Boolean) {
+    override fun clearFocus(force: Boolean) {
         // If this hierarchy had focus before clearing it, it indicates that the host view has
         // focus. So after clearing focus within the compose hierarchy, we should reset the root
         // focus modifier to "Active" to maintain consistency with the host view.
@@ -172,7 +172,7 @@
             Disabled, Inactive -> false
         }
 
-        if (focusModifier.focusNode.clearFocus(forcedClear) && rootWasFocused) {
+        if (focusModifier.focusNode.clearFocus(force) && rootWasFocused) {
             focusModifier.focusState = Active
         }
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt
index 99a0886..c11e511 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.graphics.vector
 
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
@@ -43,6 +42,7 @@
     private var cachedCanvas: Canvas? = null
     private var scopeDensity: Density? = null
     private var layoutDirection: LayoutDirection = LayoutDirection.Ltr
+    private var size: IntSize = IntSize.Zero
 
     private val cacheScope = CanvasDrawScope()
 
@@ -72,6 +72,7 @@
             mCachedImage = targetImage
             cachedCanvas = targetCanvas
         }
+        this.size = size
         cacheScope.draw(density, layoutDirection, targetCanvas, size.toSize()) {
             clear()
             block()
@@ -92,7 +93,7 @@
             "drawCachedImage must be invoked first before attempting to draw the result " +
                 "into another destination"
         }
-        target.drawImage(targetImage, Offset.Zero, alpha = alpha, colorFilter = colorFilter)
+        target.drawImage(targetImage, srcSize = size, alpha = alpha, colorFilter = colorFilter)
     }
 
     /**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequester.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequester.kt
index 9a0780f..db7c07a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequester.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequester.kt
@@ -24,20 +24,20 @@
  * This class can be used to send relocation requests. Pass it as a parameter to
  * [Modifier.relocationRequester()][relocationRequester].
  *
- * For instance, you can call [RelocationRequester.bringIntoParentBounds][bringIntoParentBounds] to
+ * For instance, you can call [RelocationRequester.bringIntoView][bringIntoView] to
  * make all the scrollable parents scroll so that the specified item is brought into parent
  * bounds. This sample demonstrates this use case:
  *
- * @sample androidx.compose.ui.samples.BringIntoParentBoundsSample
+ * @sample androidx.compose.ui.samples.BringIntoViewSample
  */
 @ExperimentalComposeUiApi
 class RelocationRequester {
     internal val modifiers: MutableVector<RelocationRequesterModifier> = mutableVectorOf()
 
     /**
-     * Bring this item into parent bounds by making all the scrollable parents scroll appropriately.
+     * Bring this item into bounds by making all the scrollable parents scroll appropriately.
      */
-    fun bringIntoParentBounds() {
-        modifiers.forEach { it.bringIntoParentBounds() }
+    fun bringIntoView() {
+        modifiers.forEach { it.bringIntoView() }
     }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
index 0a51f59..7d7b737 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
@@ -34,9 +34,15 @@
 internal class RelocationRequesterModifier : OnGloballyPositionedModifier {
     lateinit var coordinates: LayoutCoordinates
 
-    fun bringIntoParentBounds() {
-        (coordinates as LayoutNodeWrapper).findPreviousNestedScrollWrapper()
-            ?.bringIntoParentBounds(coordinates)
+    fun bringIntoView() {
+        val layoutNodeWrapper = coordinates
+        check(layoutNodeWrapper is LayoutNodeWrapper)
+
+        // Recursively scroll parents so that the item is visible.
+        layoutNodeWrapper.findPreviousNestedScrollWrapper()?.bringIntoView(coordinates)
+
+        // Ask the owner to send a request to its parents to make sure this item is visible.
+        layoutNodeWrapper.layoutNode.owner?.requestRectangleOnScreen(coordinates.boundsInRoot())
     }
 
     override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
@@ -50,7 +56,7 @@
  * Here is an example where the a [relocationRequester] can be used to bring an item into parent
  * bounds. It demonstrates how a composable can ask its parents to scroll so that the component
  * using this modifier is brought into the bounds of all its parents.
- * @sample androidx.compose.ui.samples.BringIntoParentBoundsSample
+ * @sample androidx.compose.ui.samples.BringIntoViewSample
  *
  * @param relocationRequester an instance of [RelocationRequester]. This hoisted object can be
  * used to send relocation requests to parents of the current composable.
@@ -73,7 +79,7 @@
 // Scroll this nested scroll parent to bring the child into view. Then find the nested scroll parent
 // of this nested scroll parent and ask them to do the same. This results in scrolls propagating
 // up to all the nested scroll parents to bring the specified child into view.
-private fun NestedScrollDelegatingWrapper.bringIntoParentBounds(child: LayoutCoordinates) {
+private fun NestedScrollDelegatingWrapper.bringIntoView(child: LayoutCoordinates) {
     val childBounds = localBoundingBoxOf(child, false)
     val offset = Offset(
         calculateOffset(childBounds.left, childBounds.right, size.width.toFloat()),
@@ -84,7 +90,7 @@
     //  specific parents.
     modifier.connection.onPostScroll(Zero, offset, Drag)
 
-    wrappedBy?.findPreviousNestedScrollWrapper()?.bringIntoParentBounds(child)
+    wrappedBy?.findPreviousNestedScrollWrapper()?.bringIntoView(child)
 }
 
 // Calculate the offset needed to bring one of the edges into view. The leadingEdge is the side
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index 92a69d7..9c509bc 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -202,7 +202,8 @@
         val itemIndex = root.foldedChildren.indexOf(node)
         if (itemIndex < currentIndex) {
             throw IllegalArgumentException(
-                "$slotId was already used with subcompose during this measuring pass"
+                "Key $slotId was already used. If you are using LazyColumn/Row please make sure " +
+                    "you provide a unique key for each item."
             )
         }
         if (currentIndex != itemIndex) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt
index 050f56b..0a249c6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedFocusNode.kt
@@ -92,7 +92,7 @@
         when (focusState) {
             // If this node is focused, set the focus on the root layoutNode before removing it.
             Active, Captured -> {
-                layoutNode.owner?.focusManager?.clearFocus(forcedClear = true)
+                layoutNode.owner?.focusManager?.clearFocus(force = true)
             }
             // Propagate the state of the next focus node to any focus observers in the hierarchy.
             ActiveParent -> {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
index 5a70959..212bb6c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
@@ -21,6 +21,7 @@
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.input.key.KeyEvent
@@ -184,6 +185,12 @@
      */
     fun getFocusDirection(keyEvent: KeyEvent): FocusDirection?
 
+    /**
+     * Request that a rectangle of this owner be visible on the screen, scrolling if necessary just
+     * enough.
+     */
+    fun requestRectangleOnScreen(rect: Rect)
+
     val measureIteration: Long
 
     /**
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt
index cf41892..b2fe299 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt
@@ -17,7 +17,7 @@
 
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.MenuBar
 import java.awt.event.MouseListener
 import java.awt.event.MouseMotionListener
 import java.awt.image.BufferedImage
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt
index 035aff4..2cdac33 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt
@@ -15,7 +15,7 @@
  */
 package androidx.compose.desktop
 
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.MenuBar
 
 object AppManager {
 
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt
index ac4cfd6..80252f0 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt
@@ -22,7 +22,7 @@
 import androidx.compose.ui.platform.Keyboard
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.MenuBar
 import java.awt.Container
 import java.awt.Frame
 import java.awt.event.ComponentAdapter
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.desktop.kt
index 32b021f..74272e4 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.desktop.kt
@@ -36,6 +36,7 @@
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusManagerImpl
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.DesktopCanvas
 import androidx.compose.ui.input.key.Key.Companion.Back
@@ -286,6 +287,10 @@
         }
     }
 
+    override fun requestRectangleOnScreen(rect: Rect) {
+        // TODO: Scroll the owner to bring the specified rectangle into view.
+    }
+
     override fun calculatePositionInWindow(localPosition: Offset): Offset = localPosition
 
     override fun calculateLocalPosition(positionInWindow: Offset): Offset = positionInWindow
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/DesktopDialog.desktop.kt
similarity index 97%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/DesktopDialog.desktop.kt
index 4a9174a..a16f12a 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/DesktopDialog.desktop.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
 
 import androidx.compose.desktop.AppWindow
 import androidx.compose.desktop.LocalAppWindow
@@ -22,8 +22,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.rememberCompositionContext
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCompositionContext
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import java.awt.image.BufferedImage
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuBar.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuBar.desktop.kt
similarity index 96%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuBar.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuBar.desktop.kt
index 3672743..8f65fcf 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuBar.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuBar.desktop.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 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.
@@ -13,11 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
 
 import org.jetbrains.skiko.Library
-import java.awt.event.ActionListener
 import java.awt.event.ActionEvent
+import java.awt.event.ActionListener
 import javax.swing.JMenu
 import javax.swing.JMenuBar
 import javax.swing.JMenuItem
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuItem.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuItem.desktop.kt
similarity index 95%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuItem.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuItem.desktop.kt
index d7c732a..031c03b 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuItem.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuItem.desktop.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
 
 import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.nativeKeyCode
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Tray.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/Tray.desktop.kt
similarity index 97%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Tray.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/Tray.desktop.kt
index 5e51ec8..6affd42 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Tray.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/Tray.desktop.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 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.
@@ -13,15 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
 
-import java.awt.event.ActionListener
-import java.awt.event.ActionEvent
 import java.awt.Image
 import java.awt.PopupMenu
 import java.awt.SystemTray
 import java.awt.TrayIcon
 import java.awt.TrayIcon.MessageType
+import java.awt.event.ActionEvent
+import java.awt.event.ActionListener
 
 /**
  * Tray is class for working with the system tray.
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
index 701b47e..e18c76c 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
@@ -105,7 +105,7 @@
         }
 
         // Act.
-        focusManager.clearFocus(forcedClear = true)
+        focusManager.clearFocus(force = true)
 
         // Assert.
         assertThat(focusModifier.focusState).isEqualTo(
@@ -130,7 +130,7 @@
         }
 
         // Act.
-        focusManager.clearFocus(forcedClear = false)
+        focusManager.clearFocus(force = false)
 
         // Assert.
         assertThat(focusModifier.focusState).isEqualTo(
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index e980555..4206975 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.geometry.MutableRect
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.TransformOrigin
@@ -1889,6 +1890,10 @@
         TODO("Not yet implemented")
     }
 
+    override fun requestRectangleOnScreen(rect: Rect) {
+        TODO("Not yet implemented")
+    }
+
     override var measureIteration: Long = 0
     override val viewConfiguration: ViewConfiguration
         get() = TODO("Not yet implemented")
diff --git a/core/core-animation/build.gradle b/core/core-animation/build.gradle
index d7061d3..d963b61 100644
--- a/core/core-animation/build.gradle
+++ b/core/core-animation/build.gradle
@@ -25,7 +25,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.2.0")
     implementation("androidx.core:core:1.3.1")
     implementation("androidx.collection:collection:1.1.0")
 
diff --git a/core/core-animation/lint-baseline.xml b/core/core-animation/lint-baseline.xml
index 35d7b15..13d37ce 100644
--- a/core/core-animation/lint-baseline.xml
+++ b/core/core-animation/lint-baseline.xml
@@ -90,17 +90,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.animation.PathUtils is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return path.approximate(precision);"
-        errorLine2="                        ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/animation/PathUtils.java"
-            line="34"
-            column="25"/>
-    </issue>
-
-    <issue
         id="KotlinPropertyAccess"
         message="The getter return type (`PropertyValuesHolder[]`) and setter parameter type (`PropertyValuesHolder...`) getter and setter methods for property `values` should have exactly the same type to allow be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
         errorLine1="    public PropertyValuesHolder[] getValues() {"
diff --git a/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java b/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java
index 730fc21..f55e292 100644
--- a/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java
+++ b/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java
@@ -19,6 +19,9 @@
 import android.graphics.PathMeasure;
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
+import androidx.annotation.RequiresApi;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -31,7 +34,7 @@
 
     static float[] createKeyFrameData(Path path, float precision) {
         if (Build.VERSION.SDK_INT >= 26) {
-            return path.approximate(precision);
+            return Api26Impl.approximate(path, precision);
         } else {
             // Measure the total length the whole path.
             final PathMeasure measureForTotalLength = new PathMeasure(path, false);
@@ -162,4 +165,15 @@
         data.add(y);
     }
 
+    @RequiresApi(26)
+    static class Api26Impl {
+        private Api26Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static float[] approximate(Path path, float acceptableError) {
+            return path.approximate(acceptableError);
+        }
+    }
 }
diff --git a/core/core-google-shortcuts/build.gradle b/core/core-google-shortcuts/build.gradle
index 7f7bf68..ea77000 100644
--- a/core/core-google-shortcuts/build.gradle
+++ b/core/core-google-shortcuts/build.gradle
@@ -32,7 +32,7 @@
 }
 
 dependencies {
-    api("androidx.core:core:1.6.0-beta01")
+    api(project(":core:core"))
 
     implementation("com.google.firebase:firebase-appindexing:19.2.0")
     implementation("com.google.crypto.tink:tink-android:1.5.0")
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 83d6a2d..2cf275c 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1542,7 +1542,6 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
-    method @ChecksSdkIntAtLeast(codename="T") public static boolean isAtLeastT();
   }
 
   public final class CancellationSignal {
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index ebb5dfb..3934ab1 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1540,7 +1540,6 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
-    method @ChecksSdkIntAtLeast(codename="T") public static boolean isAtLeastT();
   }
 
   public final class CancellationSignal {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 34557d9..46113a6 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1861,7 +1861,6 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
-    method @ChecksSdkIntAtLeast(codename="T") public static boolean isAtLeastT();
   }
 
   public final class CancellationSignal {
diff --git a/core/core/lint-baseline.xml b/core/core/lint-baseline.xml
index 63626a6..cbe1a6d 100644
--- a/core/core/lint-baseline.xml
+++ b/core/core/lint-baseline.xml
@@ -46,39 +46,6 @@
     </issue>
 
     <issue
-        id="BanSynchronizedMethods"
-        message="Use of synchronized methods is not recommended"
-        errorLine1="    /**"
-        errorLine2="    ^">
-        <location
-            file="src/main/java/androidx/core/widget/ContentLoadingProgressBar.java"
-            line="92"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="BanSynchronizedMethods"
-        message="Use of synchronized methods is not recommended"
-        errorLine1="    /**"
-        errorLine2="    ^">
-        <location
-            file="src/main/java/androidx/core/widget/ContentLoadingProgressBar.java"
-            line="118"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="BanSynchronizedMethods"
-        message="Use of synchronized methods is not recommended"
-        errorLine1="    private synchronized static File createFilesDir(File file) {"
-        errorLine2="    ^">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="602"
-            column="5"/>
-    </issue>
-
-    <issue
         id="BanUncheckedReflection"
         message="Calling Method.invoke without an SDK check"
         errorLine1="                    requestRelaunchActivityMethod.invoke(activityThread,"
diff --git a/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java b/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java
new file mode 100644
index 0000000..a333e5e
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 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.core.os;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link BuildCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BuildCompatTest {
+    @Test
+    public void isAtLeastPreReleaseCodename() {
+        assertTrue(BuildCompat.isAtLeastPreReleaseCodename("S", "S"));
+        assertTrue(BuildCompat.isAtLeastPreReleaseCodename("T", "S"));
+        assertFalse(BuildCompat.isAtLeastPreReleaseCodename("S", "T"));
+
+        assertTrue(BuildCompat.isAtLeastPreReleaseCodename("OMR1", "O"));
+        assertFalse(BuildCompat.isAtLeastPreReleaseCodename("O", "OMR1"));
+
+        assertTrue(BuildCompat.isAtLeastPreReleaseCodename("OMR1", "OMR1"));
+        assertTrue(BuildCompat.isAtLeastPreReleaseCodename("OMR2", "OMR1"));
+        assertFalse(BuildCompat.isAtLeastPreReleaseCodename("OMR1", "OMR2"));
+
+        assertFalse(BuildCompat.isAtLeastPreReleaseCodename("S", "REL"));
+
+        assertFalse(BuildCompat.isAtLeastPreReleaseCodename("RMR1", "REL"));
+    }
+
+}
diff --git a/core/core/src/main/java/androidx/core/content/ContextCompat.java b/core/core/src/main/java/androidx/core/content/ContextCompat.java
index 882f35b..2f54ab4 100644
--- a/core/core/src/main/java/androidx/core/content/ContextCompat.java
+++ b/core/core/src/main/java/androidx/core/content/ContextCompat.java
@@ -158,6 +158,9 @@
 
     private static final Object sLock = new Object();
 
+    // Lock that provides similar functionality to ContextImpl.mSync.
+    private static final Object sSync = new Object();
+
     private static TypedValue sTempValue;
 
     /**
@@ -599,18 +602,23 @@
         }
     }
 
-    private synchronized static File createFilesDir(File file) {
-        if (!file.exists()) {
-            if (!file.mkdirs()) {
-                if (file.exists()) {
-                    // spurious failure; probably racing with another process for this app
+    private static File createFilesDir(File file) {
+        // In the platform, all operations on Context that involve creating files (codeCacheDir,
+        // noBackupFilesDir, etc.) are synchronized on a single lock owned by the Context. So, if
+        // we lock on a single static lock owned by ContextCompat then we're a bit too broad but
+        // at least we'll provide similar guarantees.
+        synchronized (sSync) {
+            if (!file.exists()) {
+                if (file.mkdirs()) {
                     return file;
+                } else {
+                    // There used to be another check for file.exists() here, but that was a
+                    // side-effect of improper synchronization.
+                    Log.w(TAG, "Unable to create files subdir " + file.getPath());
                 }
-                Log.w(TAG, "Unable to create files subdir " + file.getPath());
-                return null;
             }
+            return file;
         }
-        return file;
     }
 
     /**
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.java b/core/core/src/main/java/androidx/core/os/BuildCompat.java
index 389622f..7df4319 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.java
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.java
@@ -20,6 +20,8 @@
 import android.os.Build.VERSION;
 
 import androidx.annotation.ChecksSdkIntAtLeast;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 
 /**
  * This class contains additional platform version checking methods for targeting pre-release
@@ -30,6 +32,28 @@
     }
 
     /**
+     * Checks if the codename is a matching or higher version than the given build value.
+     * @param codename the requested build codename, e.g. {@code "O"} or {@code "OMR1"}
+     * @param buildCodename the value of {@link VERSION.CODENAME}
+     *
+     * @return {@code true} if APIs from the requested codename are available in the build.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    protected static boolean isAtLeastPreReleaseCodename(@NonNull String codename,
+            @NonNull String buildCodename) {
+
+        // Special case "REL", which means the build is not a pre-release build.
+        if ("REL".equals(buildCodename)) {
+            return false;
+        }
+
+        // Otherwise lexically compare them.
+        return codename.compareTo(buildCodename) >= 0;
+    }
+
+    /**
      * Checks if the device is running on the Android N release or newer.
      *
      * @return {@code true} if N APIs are available for use
@@ -139,21 +163,6 @@
      */
     @ChecksSdkIntAtLeast(codename = "S")
     public static boolean isAtLeastS() {
-        return VERSION.CODENAME.equals("S") || VERSION.CODENAME.equals("T");
-    }
-
-    /**
-     * Checks if the device is running on a pre-release version of Android T or a release version of
-     * Android T or newer.
-     * <p>
-     * <strong>Note:</strong> When Android T is finalized for release, this method will be
-     * deprecated and all calls should be replaced with {@code Build.VERSION.SDK_INT >=
-     * Build.VERSION_CODES.T}.
-     *
-     * @return {@code true} if T APIs are available for use, {@code false} otherwise
-     */
-    @ChecksSdkIntAtLeast(codename = "T")
-    public static boolean isAtLeastT() {
-        return VERSION.CODENAME.equals("T");
+        return isAtLeastPreReleaseCodename("S", VERSION.CODENAME);
     }
 }
diff --git a/core/core/src/main/java/androidx/core/view/GestureDetectorCompat.java b/core/core/src/main/java/androidx/core/view/GestureDetectorCompat.java
index 755e44d..77c59b6 100644
--- a/core/core/src/main/java/androidx/core/view/GestureDetectorCompat.java
+++ b/core/core/src/main/java/androidx/core/view/GestureDetectorCompat.java
@@ -59,7 +59,6 @@
         private int mMinimumFlingVelocity;
         private int mMaximumFlingVelocity;
 
-        private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
         private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
         private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
 
@@ -319,7 +318,7 @@
                     if (mIsLongpressEnabled) {
                         mHandler.removeMessages(LONG_PRESS);
                         mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
-                                + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
+                                + TAP_TIMEOUT + ViewConfiguration.getLongPressTimeout());
                     }
                     mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
                             mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
diff --git a/core/core/src/main/java/androidx/core/widget/ContentLoadingProgressBar.java b/core/core/src/main/java/androidx/core/widget/ContentLoadingProgressBar.java
index e24b6a1..2eb84ef 100644
--- a/core/core/src/main/java/androidx/core/widget/ContentLoadingProgressBar.java
+++ b/core/core/src/main/java/androidx/core/widget/ContentLoadingProgressBar.java
@@ -23,44 +23,35 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
 
 /**
  * ContentLoadingProgressBar implements a ProgressBar that waits a minimum time to be
  * dismissed before showing. Once visible, the progress bar will be visible for
  * a minimum amount of time to avoid "flashes" in the UI when an event could take
- * a largely variable time to complete (from none, to a user perceivable amount)
+ * a largely variable time to complete (from none, to a user perceivable amount).
  */
 public class ContentLoadingProgressBar extends ProgressBar {
-    private static final int MIN_SHOW_TIME = 500; // ms
-    private static final int MIN_DELAY = 500; // ms
+    private static final int MIN_SHOW_TIME_MS = 500;
+    private static final int MIN_DELAY_MS = 500;
 
+    // These fields should only be accessed on the UI thread.
     long mStartTime = -1;
-
     boolean mPostedHide = false;
-
     boolean mPostedShow = false;
-
     boolean mDismissed = false;
 
-    private final Runnable mDelayedHide = new Runnable() {
-
-        @Override
-        public void run() {
-            mPostedHide = false;
-            mStartTime = -1;
-            setVisibility(View.GONE);
-        }
+    private final Runnable mDelayedHide = () -> {
+        mPostedHide = false;
+        mStartTime = -1;
+        setVisibility(View.GONE);
     };
 
-    private final Runnable mDelayedShow = new Runnable() {
-
-        @Override
-        public void run() {
-            mPostedShow = false;
-            if (!mDismissed) {
-                mStartTime = System.currentTimeMillis();
-                setVisibility(View.VISIBLE);
-            }
+    private final Runnable mDelayedShow = () -> {
+        mPostedShow = false;
+        if (!mDismissed) {
+            mStartTime = System.currentTimeMillis();
+            setVisibility(View.VISIBLE);
         }
     };
 
@@ -93,13 +84,23 @@
      * Hide the progress view if it is visible. The progress view will not be
      * hidden until it has been shown for at least a minimum show time. If the
      * progress view was not yet visible, cancels showing the progress view.
+     * <p>
+     * This method may be called off the UI thread.
      */
-    public synchronized void hide() {
+    public void hide() {
+        // This method used to be synchronized, presumably so that it could be safely called off
+        // the UI thread; however, the referenced fields were still accessed both on and off the
+        // UI thread, e.g. not thread-safe. Now we hand-off everything to the UI thread.
+        post(this::hideOnUiThread);
+    }
+
+    @UiThread
+    private void hideOnUiThread() {
         mDismissed = true;
         removeCallbacks(mDelayedShow);
         mPostedShow = false;
         long diff = System.currentTimeMillis() - mStartTime;
-        if (diff >= MIN_SHOW_TIME || mStartTime == -1) {
+        if (diff >= MIN_SHOW_TIME_MS || mStartTime == -1) {
             // The progress spinner has been shown long enough
             // OR was not shown yet. If it wasn't shown yet,
             // it will just never be shown.
@@ -109,7 +110,7 @@
             // so put a delayed message in to hide it when its been
             // shown long enough.
             if (!mPostedHide) {
-                postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
+                postDelayed(mDelayedHide, MIN_SHOW_TIME_MS - diff);
                 mPostedHide = true;
             }
         }
@@ -118,15 +119,25 @@
     /**
      * Show the progress view after waiting for a minimum delay. If
      * during that time, hide() is called, the view is never made visible.
+     * <p>
+     * This method may be called off the UI thread.
      */
-    public synchronized void show() {
+    public void show() {
+        // This method used to be synchronized, presumably so that it could be safely called off
+        // the UI thread; however, the referenced fields were still accessed both on and off the
+        // UI thread, e.g. not thread-safe. Now we hand-off everything to the UI thread.
+        post(this::showOnUiThread);
+    }
+
+    @UiThread
+    private void showOnUiThread() {
         // Reset the start time.
         mStartTime = -1;
         mDismissed = false;
         removeCallbacks(mDelayedHide);
         mPostedHide = false;
         if (!mPostedShow) {
-            postDelayed(mDelayedShow, MIN_DELAY);
+            postDelayed(mDelayedShow, MIN_DELAY_MS);
             mPostedShow = true;
         }
     }
diff --git a/development/build_log_simplifier/message-flakes.ignore b/development/build_log_simplifier/message-flakes.ignore
index 843e9d4..f19cee7 100644
--- a/development/build_log_simplifier/message-flakes.ignore
+++ b/development/build_log_simplifier/message-flakes.ignore
@@ -75,3 +75,29 @@
 warning: ATTENTION!
 # b/185474400
 at org.gradle.*
+# > Task :internal-testutils-common:lintAnalyze
+Scanning .*:
+Failure reading binary cache file .*\.android\/cache\/api\-versions\-[0-9]+\-[0-9A-Z]+rev[0-9]+\.bin
+Please delete the file and restart the IDE\/lint\: .*\.android\/cache\/api\-versions\-[0-9]+\-[0-9]+rev[0-9]+\.bin
+java\.io\.FileNotFoundException\: .*\.android\/cache\/api\-versions\-[0-9]+\-[0-9]+rev[0-9]+\.bin \(No such file or directory\)
+at com\.google\.common\.io\.Files\$FileByteSource\.openStream\(Files\.java\:[0-9]+\)
+at com\.google\.common\.io\.Files\$FileByteSource\.read\(Files\.java\:[0-9]+\)
+at com\.google\.common\.io\.Files\.toByteArray\(Files\.java\:[0-9]+\)
+at com\.android\.tools\.lint\.checks\.ApiDatabase\.readData\(ApiDatabase\.java\:[0-9]+\)
+at com\.android\.tools\.lint\.checks\.ApiLookup\.\<init\>\(ApiLookup\.java\:[0-9]+\)
+at com\.android\.tools\.lint\.checks\.ApiLookup\.get\(ApiLookup\.java\:[0-9]+\)
+at com\.android\.tools\.lint\.checks\.ApiDetector\.beforeCheckRootProject\(ApiDetector\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.client\.api\.LintDriver\.checkProject\(LintDriver\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.client\.api\.LintDriver\.checkProjectRoot\(LintDriver\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.client\.api\.LintDriver\.access\$checkProjectRoot\(LintDriver\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.client\.api\.LintDriver\$analyzeOnly\$[0-9]+\.invoke\(LintDriver\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.client\.api\.LintDriver\.doAnalyze\(LintDriver\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.client\.api\.LintDriver\.analyzeOnly\(LintDriver\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.LintCliClient\$analyzeOnly\$[0-9]+\.invoke\(LintCliClient\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.LintCliClient\.run\(LintCliClient\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.LintCliClient\.run\$default\(LintCliClient\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.LintCliClient\.analyzeOnly\(LintCliClient\.kt\:[0-9]+\)
+at com\.android\.tools\.lint\.Main\.run\(Main\.java\:[0-9]+\)
+at com\.android\.build\.gradle\.internal\.lint\.AndroidLintWorkAction\.invokeLintMainRunMethod\(AndroidLintWorkAction\.kt\:[0-9]+\)
+at com\.android\.build\.gradle\.internal\.lint\.AndroidLintWorkAction\.runLint\(AndroidLintWorkAction\.kt\:[0-9]+\)
+at com\.android\.build\.gradle\.internal\.lint\.AndroidLintWorkAction\.execute\(AndroidLintWorkAction\.kt\:[0-9]+\)
diff --git a/development/referenceDocs/stageReferenceDocsWithDackka.sh b/development/referenceDocs/stageReferenceDocsWithDackka.sh
index b5689aa..cc34502 100755
--- a/development/referenceDocs/stageReferenceDocsWithDackka.sh
+++ b/development/referenceDocs/stageReferenceDocsWithDackka.sh
@@ -26,6 +26,7 @@
 #  "collection"
   "navigation"
   "paging"
+  "window"
 )
 readonly kotlinLibraryDirs=(
 #  "benchmark"
@@ -33,6 +34,7 @@
 #  "collection"
   "navigation"
 #  "paging"
+  "window"
 )
 
 
diff --git a/development/versionCatalogMigrate.sh b/development/versionCatalogMigrate.sh
index 1010130..da5fa97 100755
--- a/development/versionCatalogMigrate.sh
+++ b/development/versionCatalogMigrate.sh
@@ -3,6 +3,7 @@
 find -iname build.gradle | xargs sed -i "s/AGP_LATEST/libs.androidGradlePlugin/"
 find -iname build.gradle | xargs sed -i "s/LINT_CORE/libs.androidLint/"
 find -iname build.gradle | xargs sed -i "s/LINT_API_LATEST/libs.androidLintApi/"
+find -iname build.gradle | xargs sed -i "s/LINT_API_MIN/libs.androidLintMinApi/"
 find -iname build.gradle | xargs sed -i "s/LINT_TESTS/libs.androidLintTests/"
 find -iname build.gradle | xargs sed -i "s/AUTO_COMMON/libs.autoCommon/"
 find -iname build.gradle | xargs sed -i "s/AUTO_SERVICE_ANNOTATIONS/libs.autoServiceAnnotations/"
@@ -64,9 +65,6 @@
 find -iname build.gradle | xargs sed -i "s/KOTLIN_TEST_JS/libs.kotlinTestJs/"
 find -iname build.gradle | xargs sed -i "s/KOTLIN_TEST/libs.kotlinTest/"
 find -iname build.gradle | xargs sed -i "s/KOTLIN_REFLECT/libs.kotlinReflect/"
-find -iname build.gradle | xargs sed -i "s/KOTLINPOET_METADATA/libs.kotlinPoetMetadata/"
-find -iname build.gradle | xargs sed -i "s/KOTLINPOET_METADATA_SPECS/libs.kotlinPoetMetadataSpecs/"
-find -iname build.gradle | xargs sed -i "s/KOTLINPOET_CLASSINSPECTOR_ELEMENTS/libs.kotlinPoetClassinspector/"
 find -iname build.gradle | xargs sed -i "s/KOTLINPOET/libs.kotlinPoet/"
 find -iname build.gradle | xargs sed -i "s/LEAKCANARY_INSTRUMENTATION/libs.leakcanaryInstrumentation/"
 find -iname build.gradle | xargs sed -i "s/LEAKCANARY/libs.leakcanary/"
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index e77bab7..1ffc337 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -208,6 +208,7 @@
     docs(project(":room:room-guava"))
     docs(project(":room:room-ktx"))
     docs(project(":room:room-migration"))
+    docs(project(":room:room-paging"))
     docs(project(":room:room-runtime"))
     docs(project(":room:room-rxjava2"))
     docs(project(":room:room-rxjava3"))
@@ -215,6 +216,7 @@
     docs(project(":savedstate:savedstate"))
     docs(project(":savedstate:savedstate-ktx"))
     docs(project(":security:security-app-authenticator"))
+    docs(project(":security:security-app-authenticator-testing"))
     docs(project(":security:security-biometric"))
     docs(project(":security:security-crypto"))
     docs(project(":security:security-crypto-ktx"))
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index 2558b45..2f24481 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -43,13 +41,13 @@
     api("androidx.savedstate:savedstate-ktx:1.1.0") {
         because 'Mirror fragment dependency graph for -ktx artifacts'
     }
-    api(KOTLIN_STDLIB)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
+    api(libs.kotlinStdlib)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.fragment", module: "fragment"
     })
diff --git a/fragment/fragment-lint/build.gradle b/fragment/fragment-lint/build.gradle
index da2c726..c660740 100644
--- a/fragment/fragment-lint/build.gradle
+++ b/fragment/fragment-lint/build.gradle
@@ -25,14 +25,14 @@
 }
 
 dependencies {
-    compileOnly(LINT_API_MIN)
-    compileOnly(KOTLIN_STDLIB)
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.kotlinStdlib)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(LINT_CORE)
-    testImplementation(LINT_TESTS)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
 }
 
 androidx {
diff --git a/fragment/fragment-testing-lint/build.gradle b/fragment/fragment-testing-lint/build.gradle
index 9242788..e4170a6 100644
--- a/fragment/fragment-testing-lint/build.gradle
+++ b/fragment/fragment-testing-lint/build.gradle
@@ -17,22 +17,20 @@
 import androidx.build.LibraryGroups
 import androidx.build.LibraryType
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("kotlin")
 }
 
 dependencies {
-    compileOnly(LINT_API_MIN)
-    compileOnly(KOTLIN_STDLIB)
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.kotlinStdlib)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(LINT_CORE)
-    testImplementation(LINT_TESTS)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
 }
 
 androidx {
diff --git a/fragment/fragment-testing/build.gradle b/fragment/fragment-testing/build.gradle
index 6c9828d..6d3ac16 100644
--- a/fragment/fragment-testing/build.gradle
+++ b/fragment/fragment-testing/build.gradle
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -27,17 +25,17 @@
 
 dependencies {
     api(project(":fragment:fragment-ktx"))
-    api(ANDROIDX_TEST_CORE)
-    api(KOTLIN_STDLIB)
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    api(libs.testCore)
+    api(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
 
     lintPublish(project(":fragment:fragment-testing-lint"))
 }
diff --git a/fragment/fragment-truth/build.gradle b/fragment/fragment-truth/build.gradle
index 68c38ca..19a5892 100644
--- a/fragment/fragment-truth/build.gradle
+++ b/fragment/fragment-truth/build.gradle
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -27,14 +25,14 @@
 
 dependencies {
     api(project(":fragment:fragment-ktx"))
-    api(TRUTH)
-    api(KOTLIN_STDLIB)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    api(libs.truth)
+    api(libs.kotlinStdlib)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
     androidTestImplementation(project(":internal-testutils-truth"))
 }
 
diff --git a/fragment/fragment/api/current.txt b/fragment/fragment/api/current.txt
index 9bb98ad..f56726e 100644
--- a/fragment/fragment/api/current.txt
+++ b/fragment/fragment/api/current.txt
@@ -494,13 +494,31 @@
     method public android.view.ViewGroup? getParentContainer();
   }
 
-  public final class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+    method public int getRequestCode();
+    method public androidx.fragment.app.Fragment getTargetFragment();
   }
 
   public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+    method public boolean isVisibleToUser();
   }
 
-  public final class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
   }
 
   public abstract class Violation extends java.lang.RuntimeException {
@@ -508,6 +526,7 @@
   }
 
   public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup getContainer();
   }
 
 }
diff --git a/fragment/fragment/api/public_plus_experimental_current.txt b/fragment/fragment/api/public_plus_experimental_current.txt
index d90beb5..23c97fe 100644
--- a/fragment/fragment/api/public_plus_experimental_current.txt
+++ b/fragment/fragment/api/public_plus_experimental_current.txt
@@ -464,10 +464,6 @@
 
   public final class FragmentStrictMode {
     method public static androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onRetainInstanceUsage(androidx.fragment.app.Fragment);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onSetUserVisibleHint(androidx.fragment.app.Fragment);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onTargetFragmentUsage(androidx.fragment.app.Fragment);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onWrongFragmentContainer(androidx.fragment.app.Fragment);
     method public static void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
   }
 
@@ -498,13 +494,31 @@
     method public android.view.ViewGroup? getParentContainer();
   }
 
-  public final class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+    method public int getRequestCode();
+    method public androidx.fragment.app.Fragment getTargetFragment();
   }
 
   public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+    method public boolean isVisibleToUser();
   }
 
-  public final class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
   }
 
   public abstract class Violation extends java.lang.RuntimeException {
@@ -512,6 +526,7 @@
   }
 
   public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup getContainer();
   }
 
 }
diff --git a/fragment/fragment/api/restricted_current.txt b/fragment/fragment/api/restricted_current.txt
index 225eb79..86bd304 100644
--- a/fragment/fragment/api/restricted_current.txt
+++ b/fragment/fragment/api/restricted_current.txt
@@ -494,10 +494,6 @@
 
   public final class FragmentStrictMode {
     method public static androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onRetainInstanceUsage(androidx.fragment.app.Fragment);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onSetUserVisibleHint(androidx.fragment.app.Fragment);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onTargetFragmentUsage(androidx.fragment.app.Fragment);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onWrongFragmentContainer(androidx.fragment.app.Fragment);
     method public static void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
   }
 
@@ -528,13 +524,31 @@
     method public android.view.ViewGroup? getParentContainer();
   }
 
-  public final class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+  }
+
+  public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  }
+
+  public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+  }
+
+  public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+    method public int getRequestCode();
+    method public androidx.fragment.app.Fragment getTargetFragment();
   }
 
   public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+    method public boolean isVisibleToUser();
   }
 
-  public final class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+  public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
   }
 
   public abstract class Violation extends java.lang.RuntimeException {
@@ -542,6 +556,7 @@
   }
 
   public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+    method public android.view.ViewGroup getContainer();
   }
 
 }
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index def5fb9..d672ed1 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -1,6 +1,4 @@
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -41,27 +39,27 @@
         exclude group: "androidx.fragment", module: "fragment"
         exclude group: "androidx.activity", module: "activity"
     })
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(MULTIDEX)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.espressoCore, excludes.espresso)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.multidex)
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.fragment", module: "fragment"
     })
 
     testImplementation(project(":fragment:fragment"))
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_RULES)
-    testImplementation(TRUTH)
-    testImplementation(ROBOLECTRIC)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.testExtJunit)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRules)
+    testImplementation(libs.truth)
+    testImplementation(libs.robolectric)
 
     lintPublish(project(":fragment:fragment-lint"))
 }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt
index 39895c6..1830361 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt
@@ -214,6 +214,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
     public fun detectRetainInstanceUsage() {
         var violation: Violation? = null
@@ -223,14 +224,12 @@
             .build()
         FragmentStrictMode.setDefaultPolicy(policy)
 
-        @Suppress("DEPRECATION")
         StrictFragment().retainInstance = true
-        assertThat(violation).isInstanceOf(RetainInstanceUsageViolation::class.java)
+        assertThat(violation).isInstanceOf(SetRetainInstanceUsageViolation::class.java)
 
         violation = null
-        @Suppress("DEPRECATION")
         StrictFragment().retainInstance
-        assertThat(violation).isInstanceOf(RetainInstanceUsageViolation::class.java)
+        assertThat(violation).isInstanceOf(GetRetainInstanceUsageViolation::class.java)
     }
 
     @Test
@@ -247,6 +246,7 @@
         assertThat(violation).isInstanceOf(SetUserVisibleHintViolation::class.java)
     }
 
+    @Suppress("DEPRECATION")
     @Test
     public fun detectTargetFragmentUsage() {
         var violation: Violation? = null
@@ -256,19 +256,16 @@
             .build()
         FragmentStrictMode.setDefaultPolicy(policy)
 
-        @Suppress("DEPRECATION")
         StrictFragment().setTargetFragment(StrictFragment(), 1)
-        assertThat(violation).isInstanceOf(TargetFragmentUsageViolation::class.java)
+        assertThat(violation).isInstanceOf(SetTargetFragmentUsageViolation::class.java)
 
         violation = null
-        @Suppress("DEPRECATION")
         StrictFragment().targetFragment
-        assertThat(violation).isInstanceOf(TargetFragmentUsageViolation::class.java)
+        assertThat(violation).isInstanceOf(GetTargetFragmentUsageViolation::class.java)
 
         violation = null
-        @Suppress("DEPRECATION")
         StrictFragment().targetRequestCode
-        assertThat(violation).isInstanceOf(TargetFragmentUsageViolation::class.java)
+        assertThat(violation).isInstanceOf(GetTargetFragmentRequestCodeUsageViolation::class.java)
     }
 
     @Test
@@ -298,11 +295,13 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
     public fun detectAllowedViolations() {
         val violationClass1 = RetainInstanceUsageViolation::class.java
         val violationClass2 = SetUserVisibleHintViolation::class.java
-        val violationClassList = listOf(violationClass1, violationClass2)
+        val violationClass3 = GetTargetFragmentUsageViolation::class.java
+        val violationClassList = listOf(violationClass1, violationClass2, violationClass3)
 
         var violation: Violation? = null
         var policyBuilder = FragmentStrictMode.Policy.Builder()
@@ -314,21 +313,21 @@
         }
         FragmentStrictMode.setDefaultPolicy(policyBuilder.build())
 
-        @Suppress("DEPRECATION")
         StrictFragment().retainInstance = true
         assertThat(violation).isNotInstanceOf(violationClass1)
-        assertThat(violation).isNotInstanceOf(violationClass2)
+        assertThat(violation).isNotInstanceOf(SetRetainInstanceUsageViolation::class.java)
 
         violation = null
-        @Suppress("DEPRECATION")
         StrictFragment().retainInstance
         assertThat(violation).isNotInstanceOf(violationClass1)
+        assertThat(violation).isNotInstanceOf(GetRetainInstanceUsageViolation::class.java)
+
+        violation = null
+        StrictFragment().userVisibleHint = true
         assertThat(violation).isNotInstanceOf(violationClass2)
 
         violation = null
-        @Suppress("DEPRECATION")
-        StrictFragment().userVisibleHint = true
-        assertThat(violation).isNotInstanceOf(violationClass1)
-        assertThat(violation).isNotInstanceOf(violationClass2)
+        StrictFragment().targetFragment
+        assertThat(violation).isNotInstanceOf(violationClass3)
     }
 }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
index 1bf3be1..89d8c4f5 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -804,7 +804,9 @@
     @SuppressWarnings("ReferenceEquality, deprecation")
     @Deprecated
     public void setTargetFragment(@Nullable Fragment fragment, int requestCode) {
-        FragmentStrictMode.onTargetFragmentUsage(this);
+        if (fragment != null) {
+            FragmentStrictMode.onSetTargetFragmentUsage(this, fragment, requestCode);
+        }
         // Don't allow a caller to set a target fragment in another FragmentManager,
         // but there's a snag: people do set target fragments before fragments get added.
         // We'll have the FragmentManager check that for validity when we move
@@ -818,7 +820,7 @@
         }
 
         // Don't let someone create a cycle.
-        for (Fragment check = fragment; check != null; check = check.getTargetFragment()) {
+        for (Fragment check = fragment; check != null; check = check.getTargetFragment(false)) {
             if (check.equals(this)) {
                 throw new IllegalArgumentException("Setting " + fragment + " as the target of "
                         + this + " would create a target cycle");
@@ -852,7 +854,19 @@
     @Nullable
     @Deprecated
     final public Fragment getTargetFragment() {
-        FragmentStrictMode.onTargetFragmentUsage(this);
+        return getTargetFragment(true);
+    }
+
+    /**
+     * Use with {@param logViolations} set to {@code false} for all internal calls instead of the
+     * public {@link #getTargetFragment}.
+     */
+    @Nullable
+    private Fragment getTargetFragment(boolean logViolations) {
+        if (logViolations) {
+            FragmentStrictMode.onGetTargetFragmentUsage(this);
+        }
+
         if (mTarget != null) {
             // Ensure that any Fragment set with setTargetFragment is immediately
             // available here
@@ -875,7 +889,7 @@
      */
     @Deprecated
     final public int getTargetRequestCode() {
-        FragmentStrictMode.onTargetFragmentUsage(this);
+        FragmentStrictMode.onGetTargetFragmentRequestCodeUsage(this);
         return mTargetRequestCode;
     }
 
@@ -1224,7 +1238,7 @@
      */
     @Deprecated
     public void setRetainInstance(boolean retain) {
-        FragmentStrictMode.onRetainInstanceUsage(this);
+        FragmentStrictMode.onSetRetainInstanceUsage(this);
         mRetainInstance = retain;
         if (mFragmentManager != null) {
             if (retain) {
@@ -1251,7 +1265,7 @@
      */
     @Deprecated
     final public boolean getRetainInstance() {
-        FragmentStrictMode.onRetainInstanceUsage(this);
+        FragmentStrictMode.onGetRetainInstanceUsage(this);
         return mRetainInstance;
     }
 
@@ -1313,7 +1327,7 @@
      */
     @Deprecated
     public void setUserVisibleHint(boolean isVisibleToUser) {
-        FragmentStrictMode.onSetUserVisibleHint(this);
+        FragmentStrictMode.onSetUserVisibleHint(this, isVisibleToUser);
         if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
                 && mFragmentManager != null && isAdded() && mIsCreated) {
             mFragmentManager.performPendingDeferredStart(
@@ -2826,7 +2840,7 @@
             writer.print(prefix); writer.print("mSavedViewRegistryState=");
                     writer.println(mSavedViewRegistryState);
         }
-        Fragment target = getTargetFragment();
+        Fragment target = getTargetFragment(false);
         if (target != null) {
             writer.print(prefix); writer.print("mTarget="); writer.print(target);
                     writer.print(" mTargetRequestCode=");
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
index 5ebc691..e6f976b 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
@@ -482,19 +482,22 @@
             }
             FragmentContainer fragmentContainer = mFragment.mFragmentManager.getContainer();
             container = (ViewGroup) fragmentContainer.onFindViewById(mFragment.mContainerId);
-            if (!(container instanceof FragmentContainerView)) {
-                FragmentStrictMode.onWrongFragmentContainer(mFragment);
-            }
-            if (container == null && !mFragment.mRestored) {
-                String resName;
-                try {
-                    resName = mFragment.getResources().getResourceName(mFragment.mContainerId);
-                } catch (Resources.NotFoundException e) {
-                    resName = "unknown";
+            if (container == null) {
+                if (!mFragment.mRestored) {
+                    String resName;
+                    try {
+                        resName = mFragment.getResources().getResourceName(mFragment.mContainerId);
+                    } catch (Resources.NotFoundException e) {
+                        resName = "unknown";
+                    }
+                    throw new IllegalArgumentException("No view found for id 0x"
+                            + Integer.toHexString(mFragment.mContainerId) + " ("
+                            + resName + ") for fragment " + mFragment);
                 }
-                throw new IllegalArgumentException("No view found for id 0x"
-                        + Integer.toHexString(mFragment.mContainerId) + " ("
-                        + resName + ") for fragment " + mFragment);
+            } else {
+                if (!(container instanceof FragmentContainerView)) {
+                    FragmentStrictMode.onWrongFragmentContainer(mFragment, container);
+                }
             }
         }
         mFragment.mContainer = container;
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java
index 75ca89e..ac43754 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java
@@ -320,9 +320,10 @@
         }
     }
 
+    /** @hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static void onRetainInstanceUsage(@NonNull Fragment fragment) {
-        Violation violation = new RetainInstanceUsageViolation(fragment);
+    public static void onSetRetainInstanceUsage(@NonNull Fragment fragment) {
+        Violation violation = new SetRetainInstanceUsageViolation(fragment);
         logIfDebuggingEnabled(violation);
 
         Policy policy = getNearestPolicy(fragment);
@@ -333,9 +334,24 @@
         }
     }
 
+    /** @hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static void onSetUserVisibleHint(@NonNull Fragment fragment) {
-        Violation violation = new SetUserVisibleHintViolation(fragment);
+    public static void onGetRetainInstanceUsage(@NonNull Fragment fragment) {
+        Violation violation = new GetRetainInstanceUsageViolation(fragment);
+        logIfDebuggingEnabled(violation);
+
+        Policy policy = getNearestPolicy(fragment);
+        if (policy.mFlags.contains(Flag.DETECT_RETAIN_INSTANCE_USAGE)
+                && shouldHandlePolicyViolation(
+                policy, fragment.getClass(), violation.getClass())) {
+            handlePolicyViolation(policy, violation);
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public static void onSetUserVisibleHint(@NonNull Fragment fragment, boolean isVisibleToUser) {
+        Violation violation = new SetUserVisibleHintViolation(fragment, isVisibleToUser);
         logIfDebuggingEnabled(violation);
 
         Policy policy = getNearestPolicy(fragment);
@@ -346,9 +362,28 @@
         }
     }
 
+    /** @hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static void onTargetFragmentUsage(@NonNull Fragment fragment) {
-        Violation violation = new TargetFragmentUsageViolation(fragment);
+    public static void onSetTargetFragmentUsage(
+            @NonNull Fragment violatingFragment,
+            @NonNull Fragment targetFragment,
+            int requestCode) {
+        Violation violation = new SetTargetFragmentUsageViolation(
+                violatingFragment, targetFragment, requestCode);
+        logIfDebuggingEnabled(violation);
+
+        Policy policy = getNearestPolicy(violatingFragment);
+        if (policy.mFlags.contains(Flag.DETECT_TARGET_FRAGMENT_USAGE)
+                && shouldHandlePolicyViolation(
+                policy, violatingFragment.getClass(), violation.getClass())) {
+            handlePolicyViolation(policy, violation);
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public static void onGetTargetFragmentUsage(@NonNull Fragment fragment) {
+        Violation violation = new GetTargetFragmentUsageViolation(fragment);
         logIfDebuggingEnabled(violation);
 
         Policy policy = getNearestPolicy(fragment);
@@ -359,9 +394,26 @@
         }
     }
 
+    /** @hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static void onWrongFragmentContainer(@NonNull Fragment fragment) {
-        Violation violation = new WrongFragmentContainerViolation(fragment);
+    public static void onGetTargetFragmentRequestCodeUsage(@NonNull Fragment fragment) {
+        Violation violation = new GetTargetFragmentRequestCodeUsageViolation(fragment);
+        logIfDebuggingEnabled(violation);
+
+        Policy policy = getNearestPolicy(fragment);
+        if (policy.mFlags.contains(Flag.DETECT_TARGET_FRAGMENT_USAGE)
+                && shouldHandlePolicyViolation(
+                policy, fragment.getClass(), violation.getClass())) {
+            handlePolicyViolation(policy, violation);
+        }
+    }
+
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public static void onWrongFragmentContainer(
+            @NonNull Fragment fragment,
+            @NonNull ViewGroup container) {
+        Violation violation = new WrongFragmentContainerViolation(fragment, container);
         logIfDebuggingEnabled(violation);
 
         Policy policy = getNearestPolicy(fragment);
@@ -397,7 +449,16 @@
             @NonNull Class<? extends Violation> violationClass) {
         Set<Class<? extends Violation>> violationsToBypass =
                 policy.mAllowedViolations.get(fragmentClass);
-        return violationsToBypass == null || !violationsToBypass.contains(violationClass);
+        if (violationsToBypass == null) {
+            return true;
+        }
+
+        if (violationClass.getSuperclass() != Violation.class) {
+            if (violationsToBypass.contains(violationClass.getSuperclass())) {
+                return false;
+            }
+        }
+        return !violationsToBypass.contains(violationClass);
     }
 
     private static void handlePolicyViolation(
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentTagUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentTagUsageViolation.java
index c1f1e4b..9dffe58 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentTagUsageViolation.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentTagUsageViolation.java
@@ -26,7 +26,7 @@
 public final class FragmentTagUsageViolation extends Violation {
 
     @Nullable
-    private ViewGroup mContainer;
+    private final ViewGroup mContainer;
 
     FragmentTagUsageViolation(@NonNull Fragment fragment, @Nullable ViewGroup container) {
         super(fragment);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetRetainInstanceUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetRetainInstanceUsageViolation.java
new file mode 100644
index 0000000..6ed5a3d
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetRetainInstanceUsageViolation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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.fragment.app.strictmode;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+/** See #{@link FragmentStrictMode.Policy.Builder#detectRetainInstanceUsage()}. */
+public final class GetRetainInstanceUsageViolation extends RetainInstanceUsageViolation {
+
+    GetRetainInstanceUsageViolation(@NonNull Fragment fragment) {
+        super(fragment);
+    }
+}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetTargetFragmentRequestCodeUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetTargetFragmentRequestCodeUsageViolation.java
new file mode 100644
index 0000000..84053ee
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetTargetFragmentRequestCodeUsageViolation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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.fragment.app.strictmode;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+/** See #{@link FragmentStrictMode.Policy.Builder#detectTargetFragmentUsage()}. */
+public final class GetTargetFragmentRequestCodeUsageViolation extends TargetFragmentUsageViolation {
+
+    GetTargetFragmentRequestCodeUsageViolation(@NonNull Fragment fragment) {
+        super(fragment);
+    }
+}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetTargetFragmentUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetTargetFragmentUsageViolation.java
new file mode 100644
index 0000000..da46c84
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/GetTargetFragmentUsageViolation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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.fragment.app.strictmode;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+/** See #{@link FragmentStrictMode.Policy.Builder#detectTargetFragmentUsage()}. */
+public final class GetTargetFragmentUsageViolation extends TargetFragmentUsageViolation {
+
+    GetTargetFragmentUsageViolation(@NonNull Fragment fragment) {
+        super(fragment);
+    }
+}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/RetainInstanceUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/RetainInstanceUsageViolation.java
index 9c5b074..f7ebdc2 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/RetainInstanceUsageViolation.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/RetainInstanceUsageViolation.java
@@ -20,7 +20,7 @@
 import androidx.fragment.app.Fragment;
 
 /** See #{@link FragmentStrictMode.Policy.Builder#detectRetainInstanceUsage()}. */
-public final class RetainInstanceUsageViolation extends Violation {
+public abstract class RetainInstanceUsageViolation extends Violation {
 
     RetainInstanceUsageViolation(@NonNull Fragment fragment) {
         super(fragment);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetRetainInstanceUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetRetainInstanceUsageViolation.java
new file mode 100644
index 0000000..dbeb9be
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetRetainInstanceUsageViolation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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.fragment.app.strictmode;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+/** See #{@link FragmentStrictMode.Policy.Builder#detectRetainInstanceUsage()}. */
+public final class SetRetainInstanceUsageViolation extends RetainInstanceUsageViolation {
+
+    SetRetainInstanceUsageViolation(@NonNull Fragment fragment) {
+        super(fragment);
+    }
+}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetTargetFragmentUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetTargetFragmentUsageViolation.java
new file mode 100644
index 0000000..8d4374c
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetTargetFragmentUsageViolation.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 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.fragment.app.strictmode;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+/** See #{@link FragmentStrictMode.Policy.Builder#detectTargetFragmentUsage()}. */
+public final class SetTargetFragmentUsageViolation extends TargetFragmentUsageViolation {
+
+    private final Fragment mTargetFragment;
+    private final int mRequestCode;
+
+    SetTargetFragmentUsageViolation(
+            @NonNull Fragment violatingFragment,
+            @NonNull Fragment targetFragment,
+            int requestCode) {
+        super(violatingFragment);
+        this.mTargetFragment = targetFragment;
+        this.mRequestCode = requestCode;
+    }
+
+    @NonNull
+    public Fragment getTargetFragment() {
+        return mTargetFragment;
+    }
+
+    public int getRequestCode() {
+        return mRequestCode;
+    }
+}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetUserVisibleHintViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetUserVisibleHintViolation.java
index 8eea442..122438d 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetUserVisibleHintViolation.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/SetUserVisibleHintViolation.java
@@ -22,7 +22,18 @@
 /** See #{@link FragmentStrictMode.Policy.Builder#detectSetUserVisibleHint()}. */
 public final class SetUserVisibleHintViolation extends Violation {
 
-    SetUserVisibleHintViolation(@NonNull Fragment fragment) {
+    private final boolean mIsVisibleToUser;
+
+    SetUserVisibleHintViolation(@NonNull Fragment fragment, boolean isVisibleToUser) {
         super(fragment);
+        this.mIsVisibleToUser = isVisibleToUser;
+    }
+
+    /**
+     * Indicates what the {@code isVisibleToUser} field for the {@link Fragment} causing the
+     * Violation was being set to.
+     */
+    public boolean isVisibleToUser() {
+        return mIsVisibleToUser;
     }
 }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/TargetFragmentUsageViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/TargetFragmentUsageViolation.java
index bfec26c..b62c430 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/TargetFragmentUsageViolation.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/TargetFragmentUsageViolation.java
@@ -20,7 +20,7 @@
 import androidx.fragment.app.Fragment;
 
 /** See #{@link FragmentStrictMode.Policy.Builder#detectTargetFragmentUsage()}. */
-public final class TargetFragmentUsageViolation extends Violation {
+public abstract class TargetFragmentUsageViolation extends Violation {
 
     TargetFragmentUsageViolation(@NonNull Fragment fragment) {
         super(fragment);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/WrongFragmentContainerViolation.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/WrongFragmentContainerViolation.java
index 7c85d62..2dd613e 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/WrongFragmentContainerViolation.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/WrongFragmentContainerViolation.java
@@ -16,13 +16,27 @@
 
 package androidx.fragment.app.strictmode;
 
+import android.view.ViewGroup;
+
 import androidx.annotation.NonNull;
 import androidx.fragment.app.Fragment;
 
 /** See #{@link FragmentStrictMode.Policy.Builder#detectWrongFragmentContainer()}. */
 public final class WrongFragmentContainerViolation extends Violation {
 
-    WrongFragmentContainerViolation(@NonNull Fragment fragment) {
+    private final ViewGroup mContainer;
+
+    WrongFragmentContainerViolation(@NonNull Fragment fragment, @NonNull ViewGroup container) {
         super(fragment);
+        this.mContainer = container;
+    }
+
+    /**
+     * Gets the container that the {@link Fragment} causing the Violation was
+     * being added to.
+     */
+    @NonNull
+    public ViewGroup getContainer() {
+        return mContainer;
     }
 }
diff --git a/fragment/integration-tests/testapp/build.gradle b/fragment/integration-tests/testapp/build.gradle
index 9220e1f..76b4948 100644
--- a/fragment/integration-tests/testapp/build.gradle
+++ b/fragment/integration-tests/testapp/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -34,7 +32,7 @@
 }
 
 dependencies {
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation(project(":fragment:fragment-ktx"))
     implementation("androidx.transition:transition:1.3.0")
     implementation("androidx.recyclerview:recyclerview:1.1.0")
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 3d22993..5493c52 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,15 +11,16 @@
 androidStudio = "2020.3.1.15"
 # -----------------------------------------------------------------------------
 
+androidLintMin = "26.3.0"
 androidxTest = "1.3.0"
 androidxTestExt = "1.1.2"
 autoService = "1.0-rc6"
 autoValue = "1.6.3"
 dexmaker = "2.25.0"
 espresso = "3.3.0"
-hilt = "2.35"
+hilt = "2.36"
 incap = "0.2"
-kotlin = "1.5.0"
+kotlin = "1.5.10"
 kotlinCompileTesting = "1.4.0"
 kotlinCoroutines = "1.4.3"
 ksp = "1.5.0-1.0.0-alpha10"
@@ -31,7 +32,9 @@
 [libraries]
 androidGradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" }
 androidLint = { module = "com.android.tools.lint:lint", version.ref = "androidLint" }
+androidLintMin = { module = "com.android.tools.lint:lint", version.ref = "androidLintMin" }
 androidLintApi = { module = "com.android.tools.lint:lint-api", version.ref = "androidLint" }
+androidLintMinApi = { module = "com.android.tools.lint:lint-api", version.ref = "androidLintMin" }
 androidLintTests = { module = "com.android.tools.lint:lint-tests", version.ref = "androidLint" }
 autoCommon = { module = "com.google.auto:auto-common", version = "0.11" }
 autoServiceAnnotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoService" }
@@ -99,13 +102,13 @@
 kotlinTestJs = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref = "kotlin" }
 kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
 kotlinPoet = { module = "com.squareup:kotlinpoet", version = "1.8.0" }
-kgpLeakPatcher = { module = "dev.zacsweers:kgp-150-leak-patcher", version="1.1.0" }
 ksp = { module = "com.google.devtools.ksp:symbol-processing", version.ref = "ksp" }
 kspApi = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
 kspGradlePlugin = { module = "com.google.devtools.ksp:symbol-processing-gradle-plugin", version.ref = "ksp" }
 leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
 leakcanaryInstrumentation = { module = "com.squareup.leakcanary:leakcanary-android-instrumentation", version.ref = "leakcanary" }
 material = { module = "com.google.android.material:material", version = "1.2.1" }
+mlkitBarcode = { module = "com.google.android.gms:play-services-mlkit-barcode-scanning", version = "16.1.4" }
 mockitoCore = { module = "org.mockito:mockito-core", version.ref = "mockito" }
 mockitoAndroid = { module = "org.mockito:mockito-android", version.ref = "mockito" }
 mockitoKotlin = { module = "com.nhaarman.mockitokotlin2:mockito-kotlin", version = "2.1.0" }
diff --git a/jetifier/jetifier/processor/build.gradle b/jetifier/jetifier/processor/build.gradle
index 39eeeaa..71565e7 100644
--- a/jetifier/jetifier/processor/build.gradle
+++ b/jetifier/jetifier/processor/build.gradle
@@ -32,8 +32,11 @@
     api("org.ow2.asm:asm-commons:8.0.1")
     api("org.jdom:jdom2:2.0.6")
     api(KOTLIN_STDLIB)
+    api(KOTLIN_METADATA_JVM)
     testImplementation("junit:junit:4.12")
     testImplementation(TRUTH)
+    testImplementation(KOTLIN_REFLECT)
+    testImplementation(KOTLIN_COMPILE_TESTING)
 }
 
 androidx {
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
index 7c32c73..2fb2197 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
@@ -24,10 +24,11 @@
 import com.android.tools.build.jetifier.processor.archive.ArchiveFile
 import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
 import com.android.tools.build.jetifier.processor.archive.FileSearchResult
-import com.android.tools.build.jetifier.processor.com.android.tools.build.jetifier.processor.transform.java.JavaTransformer
 import com.android.tools.build.jetifier.processor.transform.TransformationContext
 import com.android.tools.build.jetifier.processor.transform.Transformer
 import com.android.tools.build.jetifier.processor.transform.bytecode.ByteCodeTransformer
+import com.android.tools.build.jetifier.processor.transform.java.JavaTransformer
+import com.android.tools.build.jetifier.processor.transform.metainf.KotlinModuleTransformer
 import com.android.tools.build.jetifier.processor.transform.metainf.MetaInfTransformer
 import com.android.tools.build.jetifier.processor.transform.pom.PomDocument
 import com.android.tools.build.jetifier.processor.transform.pom.PomScanner
@@ -59,7 +60,8 @@
             ByteCodeTransformer(context),
             XmlResourcesTransformer(context),
             ProGuardTransformer(context),
-            JavaTransformer(context)
+            JavaTransformer(context),
+            KotlinModuleTransformer(context)
         )
 
         /**
@@ -70,7 +72,8 @@
             ByteCodeTransformer(context),
             XmlResourcesTransformer(context),
             ProGuardTransformer(context),
-            MetaInfTransformer(context)
+            MetaInfTransformer(context),
+            KotlinModuleTransformer(context)
         )
 
         /**
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
index 9b5f12e2..1546bed 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
@@ -119,4 +119,8 @@
             )
         }
     }
+
+    fun reportUnreadableKotlinModule(tag: String, filePath: Path) {
+        Log.e(tag, "Unreadable kotlin module medata file: %s", filePath)
+    }
 }
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
index a03c041..6427c83 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
@@ -21,7 +21,9 @@
 import com.android.tools.build.jetifier.core.utils.Log
 import com.android.tools.build.jetifier.processor.transform.TransformationContext
 import com.android.tools.build.jetifier.processor.transform.bytecode.asm.CustomRemapper
+import org.objectweb.asm.AnnotationVisitor
 import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.Opcodes
 import org.objectweb.asm.commons.ClassRemapper
 import java.nio.file.Path
 
@@ -49,12 +51,30 @@
         )
     }
 
-    private val typesMap = context.config.typesMap
-
     var changesDone = false
         private set
 
-    val classRemapper = ClassRemapper(visitor, CustomRemapper(this))
+    val remapper = CustomRemapper(this)
+    val classRemapper = object : ClassRemapper(visitor, remapper) {
+        override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
+            val annotationVisitor = super.visitAnnotation(descriptor, visible)
+            return if (descriptor == "Lkotlin/Metadata;")
+                KotlinMetadataVisitor(annotationVisitor) else annotationVisitor
+        }
+    }
+
+    inner class KotlinMetadataVisitor(
+        visitor: AnnotationVisitor
+    ) : AnnotationVisitor(Opcodes.ASM8, visitor) {
+        init {
+            remapper.onKotlinAnnotationVisitStart()
+        }
+
+        override fun visitEnd() {
+            remapper.onKotlinAnnotationVisitEnd()
+            super.visitEnd()
+        }
+    }
 
     override fun rewriteType(type: JavaType): JavaType {
         val result = context.typeRewriter.rewriteType(type)
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
index 7e34af8..3d0ed43 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
@@ -24,6 +24,17 @@
  * Extends [Remapper] to allow further customizations.
  */
 class CustomRemapper(private val remapper: CoreRemapper) : Remapper() {
+    private var inKotlinMetadata = false
+
+    fun onKotlinAnnotationVisitStart() {
+        require(!inKotlinMetadata)
+        inKotlinMetadata = true
+    }
+
+    fun onKotlinAnnotationVisitEnd() {
+        require(inKotlinMetadata)
+        inKotlinMetadata = false
+    }
 
     override fun map(typeName: String): String {
         return remapper.rewriteType(JavaType(typeName)).fullName
@@ -72,6 +83,10 @@
 
             return "L" + mapPoolReferenceType(typeDeclaration) + ";"
         }
+        if (inKotlinMetadata) {
+            return rewriteIfMethodSignature(stringVal, ::mapPoolReferenceType)
+                ?: remapper.rewriteString(stringVal)
+        }
         return remapper.rewriteString(stringVal)
     }
 }
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/KotlinMetadataUtil.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/KotlinMetadataUtil.kt
new file mode 100644
index 0000000..5e4ef1f
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/KotlinMetadataUtil.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021 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 com.android.tools.build.jetifier.processor.transform.bytecode.asm
+
+/**
+ * If the given string [signature] is in format `(ILFoo;LBar;)LResult;` it maps
+ * referenced types via [mapDeclaration]. If the given string doesn't follow this pattern,
+ * `null` is returned.
+ *
+ * Such strings occur in kotlin's [Metadata] annotation for property getters and setters and
+ * data classes.
+ */
+internal fun rewriteIfMethodSignature(
+    signature: String,
+    mapDeclaration: (String) -> String
+): String? {
+    val mapType = { declaration: String ->
+        val type = if (isArrayDeclaration(declaration)) declaration.trim('[') else declaration
+        val mapped = if (isTypeDeclaration(type)) {
+            "L${mapDeclaration(type.substring(1, type.length - 1))};"
+        } else {
+            type
+        }
+        "${"[".repeat(declaration.length - type.length)}$mapped"
+    }
+    // trying to match strings in the format `(ILFoo;LBar;)LResult;`
+    if (!signature.startsWith('(')) return null
+    val index = signature.indexOf(')')
+    if (index == -1) return null
+    val params = splitParameters(signature.substring(1, index)).joinToString("") {
+        mapType(it)
+    }
+    val returnType = signature.substring(index + 1)
+    return "($params)${mapType(returnType)}"
+}
+
+private fun splitParameters(parameters: String): List<String> {
+    val result = mutableListOf<String>()
+    val currentParam = StringBuilder(parameters.length)
+    var inClassName = false
+    for (c in parameters) {
+        currentParam.append(c)
+        inClassName = if (inClassName) c != ';' else c == 'L'
+        // add a parameter if we're no longer in class and not in array start
+        if (!inClassName && c != '[') {
+            result.add(currentParam.toString())
+            currentParam.clear()
+        }
+    }
+    return result
+}
+
+private fun isTypeDeclaration(string: String) = string.startsWith("L") && string.endsWith(";")
+private fun isArrayDeclaration(string: String) = string.startsWith("[")
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt
index 69f19fa..e8a6b6d 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt
@@ -1,4 +1,4 @@
-package com.android.tools.build.jetifier.processor.com.android.tools.build.jetifier.processor.transform.java
+package com.android.tools.build.jetifier.processor.transform.java
 
 import com.android.tools.build.jetifier.processor.archive.ArchiveFile
 import com.android.tools.build.jetifier.processor.transform.TransformationContext
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/KotlinModuleTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/KotlinModuleTransformer.kt
new file mode 100644
index 0000000..5632348
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/KotlinModuleTransformer.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 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 com.android.tools.build.jetifier.processor.transform.metainf
+
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.PackageName
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
+import kotlinx.metadata.jvm.KmPackageParts
+import kotlinx.metadata.jvm.KotlinModuleMetadata
+
+class KotlinModuleTransformer internal constructor(
+    private val context: TransformationContext
+) : Transformer {
+    override fun canTransform(file: ArchiveFile): Boolean {
+        return file.relativePath.toString().startsWith(META_INF_DIR) &&
+            file.fileName.endsWith(KOTLIN_MODULE_SUFFIX) &&
+            !file.isSingleFile
+    }
+
+    override fun runTransform(file: ArchiveFile) {
+        val module = KotlinModuleMetadata.read(file.data)?.toKmModule()
+            ?: return context.reportUnreadableKotlinModule(TAG, file.relativePath)
+        val newPackageParts = module.packageParts.map { (packageName, packageParts) ->
+            val pckg = PackageName.fromDotVersion(packageName)
+            val result = context.config.packageMap.getPackageFor(pckg)
+            val newPackageName = result?.toDotNotation()
+            if (newPackageName == null && context.config.isEligibleForRewrite(pckg)) {
+                context.reportNoPackageMappingFoundFailure(TAG, packageName, file.relativePath)
+            }
+
+            val newSingleFacades = packageParts.fileFacades.map(this::mapType).toMutableList()
+            val newMultiFacades = packageParts.multiFileClassParts.map { (key, singleFile) ->
+                mapType(key) to mapType(singleFile)
+            }.toMap().toMutableMap()
+
+            val newPackageParts = KmPackageParts(
+                newSingleFacades,
+                newMultiFacades,
+            )
+            (newPackageName ?: packageName) to newPackageParts
+        }.toMap()
+        module.packageParts.clear()
+        module.packageParts.putAll(newPackageParts)
+        file.setNewData(KotlinModuleMetadata.Writer().apply(module::accept).write().bytes)
+    }
+
+    private fun mapType(packageName: String): String {
+        val javaType = JavaType(packageName)
+        val newType = context.typeRewriter.rewriteType(javaType)
+        if (newType == null) {
+            context.reportNoMappingFoundFailure(TAG, javaType)
+        }
+        return newType?.fullName ?: packageName
+    }
+}
+
+private const val META_INF_DIR = "META-INF"
+private const val KOTLIN_MODULE_SUFFIX = ".kotlin_module"
+private const val TAG = "KotlinModuleTransformer"
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestDataClass.kt b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestDataClass.kt
new file mode 100644
index 0000000..6e8dbde
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestDataClass.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021 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.fake.lib
+
+// used in KotlinByteCodeTransformerTest as a resource
+@Suppress("unused")
+data class TestDataClass(val property: String, var children: TestDataClass? = null)
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestProperty.kt b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestProperty.kt
new file mode 100644
index 0000000..3a86d06
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestProperty.kt
@@ -0,0 +1,7 @@
+package androidx.fake.lib
+
+// used in KotlinByteCodeTransformerTest as a resource
+@Suppress("unused")
+class TestProperty {
+    var property: TestProperty? = null
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinModuleRewriteTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinModuleRewriteTest.kt
new file mode 100644
index 0000000..a0eb8a5
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinModuleRewriteTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2021 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 com.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
+import com.google.common.truth.Truth.assertThat
+import com.tschuchort.compiletesting.KotlinCompilation
+import com.tschuchort.compiletesting.SourceFile
+import com.tschuchort.compiletesting.SourceFile.Companion.kotlin
+import org.intellij.lang.annotations.Language
+import org.junit.Test
+import java.io.File
+import java.nio.file.Files
+
+/**
+ * Test that verifies that *.kotlin_module file is correctly rewritten.
+ */
+class KotlinModuleRewriteTest {
+    @Test
+    fun topLevel() {
+        val jars = originalJar(
+            kotlin(
+                "toplevel.kt",
+                """
+                    package androidx.fake.lib;
+                    
+                    fun topLevel(): Int = 10
+                """
+            )
+        )
+        testKotlinCompilation(
+            jars,
+            """
+                import android.old.fake.topLevel
+
+                fun hello() {
+                    println(topLevel())
+                }
+            """
+        )
+    }
+
+    @Test
+    fun multifile() {
+        val originalJar = originalJar(
+            kotlin(
+                "mutlifile1.kt",
+                """
+                @file:JvmName("Single")
+                @file:JvmMultifileClass
+                
+                package androidx.fake.lib
+                
+                fun stringMethod(): String = "one"
+            """
+            ),
+            kotlin(
+                "mutlifile2.kt",
+                """
+                @file:JvmName("Single")
+                @file:JvmMultifileClass
+                
+                package androidx.fake.lib
+                
+                fun intMethod(): Int = 5
+            """
+            )
+        )
+        testKotlinCompilation(
+            originalJar,
+            """
+                import android.old.fake.stringMethod
+                import android.old.fake.intMethod
+
+                fun hello() {
+                    println(stringMethod())
+                    println(intMethod())
+                }
+            """
+        )
+    }
+}
+
+private fun testKotlinCompilation(originalJar: File, @Language("kotlin") content: String) {
+    val processor = Processor.createProcessor4(KotlinTestConfig)
+    val output = Files.createTempFile("out", ".jar").toFile()
+    processor.transform2(setOf(FileMapping(originalJar, output)))
+
+    val kotlinCompilation = KotlinCompilation()
+    kotlinCompilation.sources = listOf(kotlin("test.kt", content))
+    kotlinCompilation.classpaths = listOf(output)
+    assertThat(kotlinCompilation.compile().exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
+}
+
+fun originalJar(vararg sources: SourceFile): File {
+    val originalJar = Files.createTempFile("original", ".jar").toFile()
+    val kotlinCompilation = KotlinCompilation()
+    kotlinCompilation.kotlincArguments = listOf("-d", originalJar.absolutePath)
+    kotlinCompilation.sources = sources.toList()
+    kotlinCompilation.compile()
+    assertThat(kotlinCompilation.compile().exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
+    return originalJar
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinTestConfig.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinTestConfig.kt
new file mode 100644
index 0000000..9d31e3d
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinTestConfig.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 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 com.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+
+val KotlinTestConfig = Config.fromOptional(
+    restrictToPackagePrefixes = setOf("androidx/fake"),
+    reversedRestrictToPackagesPrefixes = setOf("android/old"),
+    rulesMap = RewriteRulesMap(
+        RewriteRule("androidx/fake/lib/(.*)", "android/old/fake/{0}")
+    ),
+
+    packageMap = PackageMap(
+        listOf(PackageMap.PackageRule("androidx/fake/lib", "android/old/fake"))
+    )
+)
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/KotlinByteCodeTransformerTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/KotlinByteCodeTransformerTest.kt
new file mode 100644
index 0000000..2a76b4a
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/KotlinByteCodeTransformerTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 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 com.android.tools.build.jetifier.processor.transform.bytecode
+
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.KotlinTestConfig
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import java.io.File
+import java.net.URLClassLoader
+import java.nio.file.Paths
+import kotlin.reflect.KClass
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.full.primaryConstructor
+
+class KotlinByteCodeTransformerTest {
+    @Test
+    fun propertyTest() {
+        val testClass = transformAndLoadClass("TestProperty")
+        val testInstance = testClass.primaryConstructor!!.call()
+
+        assertThat(testClass.memberProperties).hasSize(1)
+        val property = testClass.memberProperties.first()
+        // this call will fail if metadata for this property is incorrect
+        property.call(testInstance)
+    }
+
+    @Test
+    fun dataClassTest() {
+        val testClass = transformAndLoadClass("TestDataClass")
+        // this call will fail if metadata for this data class is incorrect
+        testClass.primaryConstructor!!.call("a", null)
+    }
+}
+
+private fun transformAndLoadClass(className: String): KClass<*> {
+    val inputClassPath = "/androidx/fake/lib/$className.class"
+    val inputFile = File(KotlinByteCodeTransformerTest::class.java.getResource(inputClassPath).file)
+    val archiveFile = ArchiveFile(
+        Paths.get("androidx/fake/lib", "$className.class"),
+        inputFile.readBytes()
+    )
+
+    val context = TransformationContext(config = KotlinTestConfig)
+    val transformer = ByteCodeTransformer(context)
+    transformer.runTransform(archiveFile)
+    val bytes = archiveFile.data
+
+    val classLoader = object : URLClassLoader(emptyArray()) {
+        override fun findClass(name: String): Class<*> =
+            if (name == "android.old.fake.$className") defineClass(name, bytes, 0, bytes.size)
+            else super.findClass(name)
+    }
+    return classLoader.loadClass("android.old.fake.$className").kotlin
+}
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/SignatureMappingTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/SignatureMappingTest.kt
new file mode 100644
index 0000000..6722eeb
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/SignatureMappingTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021 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 com.android.tools.build.jetifier.processor.transform.bytecode
+
+import com.android.tools.build.jetifier.processor.transform.bytecode.asm.rewriteIfMethodSignature
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class SignatureMappingTest {
+    @Test
+    fun remapSignatureString_inKotlinMetadata() {
+        val typesMap = mapOf(
+            "android/old/Foo" to "androidx/fancy/Foo",
+            "android/old/Bar" to "androidx/fancy/Bar",
+        )
+
+        fun assertRewrite(oldSignature: String, newSignature: String) =
+            assertThat(rewriteIfMethodSignature(oldSignature) { type -> typesMap[type] ?: type })
+                .isEqualTo(newSignature)
+
+        assertRewrite("()V", "()V")
+        assertRewrite("(Landroid/old/Foo;)V", "(Landroidx/fancy/Foo;)V")
+        assertRewrite("()Landroid/old/Foo;", "()Landroidx/fancy/Foo;")
+
+        assertRewrite("(Landroid/unmapped/Foo;)V", "(Landroid/unmapped/Foo;)V")
+        assertRewrite(
+            "(Landroid/old/Bar;JLandroid/unmapped/Foo;)V",
+            "(Landroidx/fancy/Bar;JLandroid/unmapped/Foo;)V"
+        )
+        assertRewrite(
+            "(JLandroid/unmapped/Foo;I[[I)Landroid/old/Bar;",
+            "(JLandroid/unmapped/Foo;I[[I)Landroidx/fancy/Bar;"
+        )
+        assertRewrite("([Landroid/old/Foo;)V", "([Landroidx/fancy/Foo;)V")
+        assertRewrite("()[[Landroid/old/Foo;", "()[[Landroidx/fancy/Foo;")
+    }
+}
\ No newline at end of file
diff --git a/lifecycle/integration-tests/incrementality/build.gradle b/lifecycle/integration-tests/incrementality/build.gradle
index e802401..c877c03 100644
--- a/lifecycle/integration-tests/incrementality/build.gradle
+++ b/lifecycle/integration-tests/incrementality/build.gradle
@@ -16,17 +16,15 @@
 
 import androidx.build.SdkResourceGenerator
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("kotlin")
     id("AndroidXPlugin")
 }
 
 dependencies {
-    implementation(KOTLIN_STDLIB)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    implementation(libs.kotlinStdlib)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
     testImplementation(gradleTestKit())
 }
 
diff --git a/lifecycle/integration-tests/kotlintestapp/build.gradle b/lifecycle/integration-tests/kotlintestapp/build.gradle
index 7033177..bfe9cf1 100644
--- a/lifecycle/integration-tests/kotlintestapp/build.gradle
+++ b/lifecycle/integration-tests/kotlintestapp/build.gradle
@@ -18,8 +18,6 @@
 // -Dorg.gradle.debug=true
 // -Dkotlin.compiler.execution.strategy="in-process"
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -35,19 +33,19 @@
         because "Used by :activity:activity"
     }
 
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(KOTLIN_COROUTINES_ANDROID)
-    testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.kotlinCoroutinesAndroid)
+    testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
 
     androidTestImplementation(project(":lifecycle:lifecycle-runtime-testing"))
-    androidTestImplementation(KOTLIN_COROUTINES_TEST)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
-    androidTestImplementation(ANDROIDX_TEST_EXT_KTX)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(libs.kotlinCoroutinesTest)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
+    androidTestImplementation(libs.testExtJunitKtx)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.espressoCore)
 }
 
 android {
diff --git a/lifecycle/integration-tests/testapp/build.gradle b/lifecycle/integration-tests/testapp/build.gradle
index 378c58e..e2edd36 100644
--- a/lifecycle/integration-tests/testapp/build.gradle
+++ b/lifecycle/integration-tests/testapp/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -23,7 +21,7 @@
 }
 
 dependencies {
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation("androidx.fragment:fragment:1.3.0") {
         exclude group: "androidx.lifecycle", module: "lifecycle-runtime"
     }
@@ -34,13 +32,13 @@
 
     androidTestAnnotationProcessor(project(":lifecycle:lifecycle-compiler"))
     androidTestImplementation(project(":lifecycle:lifecycle-common")) // Added for b/155802460
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.espressoCore)
 
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
     testAnnotationProcessor(project(":lifecycle:lifecycle-compiler"))
 }
diff --git a/lifecycle/lifecycle-common-java8/build.gradle b/lifecycle/lifecycle-common-java8/build.gradle
index dd3021f..db21aff 100644
--- a/lifecycle/lifecycle-common-java8/build.gradle
+++ b/lifecycle/lifecycle-common-java8/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions;
-import androidx.build.AndroidXExtension;
 import androidx.build.Publish
 
 plugins {
@@ -29,8 +27,8 @@
     api(project(":lifecycle:lifecycle-common"))
     api("androidx.annotation:annotation:1.1.0")
 
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-common/build.gradle b/lifecycle/lifecycle-common/build.gradle
index 1beb36e..6392520 100644
--- a/lifecycle/lifecycle-common/build.gradle
+++ b/lifecycle/lifecycle-common/build.gradle
@@ -16,9 +16,6 @@
 
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
-
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.Publish
 
 plugins {
@@ -29,8 +26,8 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
 
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-compiler/build.gradle b/lifecycle/lifecycle-compiler/build.gradle
index f64dfa1..d272608 100644
--- a/lifecycle/lifecycle-compiler/build.gradle
+++ b/lifecycle/lifecycle-compiler/build.gradle
@@ -17,8 +17,7 @@
 import androidx.build.LibraryGroups
 import androidx.build.LibraryType
 import androidx.build.LibraryVersions
-
-import static androidx.build.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.GUAVA
 
 plugins {
     id("AndroidXPlugin")
@@ -27,11 +26,11 @@
 
 dependencies {
     implementation(project(":lifecycle:lifecycle-common"))
-    implementation(KOTLIN_STDLIB)
-    implementation(AUTO_COMMON)
-    implementation(JAVAPOET)
-    testImplementation(GOOGLE_COMPILE_TESTING)
-    testImplementation(JSR250)
+    implementation(libs.kotlinStdlib)
+    implementation(libs.autoCommon)
+    implementation(libs.javapoet)
+    testImplementation(libs.googleCompileTesting)
+    testImplementation(libs.jsr250)
 }
 
 // The following tasks are used to regenerate src/test/test-data/lib/src/test-library.jar
diff --git a/lifecycle/lifecycle-extensions/build.gradle b/lifecycle/lifecycle-extensions/build.gradle
index 12f7761..fd2e937 100644
--- a/lifecycle/lifecycle-extensions/build.gradle
+++ b/lifecycle/lifecycle-extensions/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -39,16 +37,16 @@
     api("androidx.lifecycle:lifecycle-viewmodel:2.2.0")
 
     testImplementation("androidx.arch.core:core-testing:2.1.0")
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.espressoCore)
     androidTestImplementation("androidx.appcompat:appcompat:1.0.0")
     androidTestImplementation(project(":internal-testutils-runtime"))
 }
diff --git a/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle b/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
index 193e1c7..34ff630 100644
--- a/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
@@ -26,14 +26,14 @@
 }
 
 dependencies {
-    compileOnly(LINT_API_MIN)
-    compileOnly("com.android.tools.lint:lint:$lintMinVersion")
-    compileOnly(KOTLIN_STDLIB)
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.androidLintMin)
+    compileOnly(libs.kotlinStdlib)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(LINT_CORE)
-    testImplementation(LINT_TESTS)
-    testImplementation(JUNIT)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-livedata-core-ktx/build.gradle b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
index 040abc1..ab16f08 100644
--- a/lifecycle/lifecycle-livedata-core-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -30,13 +27,13 @@
 
 dependencies {
     api(project(":lifecycle:lifecycle-livedata-core"))
-    api(KOTLIN_STDLIB)
+    api(libs.kotlinStdlib)
     testImplementation(project(":lifecycle:lifecycle-runtime"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
-    testImplementation(KOTLIN_COROUTINES_TEST)
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    testImplementation(libs.kotlinCoroutinesTest)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
 
     lintPublish(project(":lifecycle:lifecycle-livedata-core-ktx-lint"))
 }
diff --git a/lifecycle/lifecycle-livedata-core-truth/build.gradle b/lifecycle/lifecycle-livedata-core-truth/build.gradle
index c1f5446..727d7b7 100644
--- a/lifecycle/lifecycle-livedata-core-truth/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-truth/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -27,10 +26,10 @@
 
 dependencies {
     api(project(":lifecycle:lifecycle-livedata-core-ktx"))
-    api(TRUTH)
-    api(KOTLIN_STDLIB)
-    testImplementation(TRUTH)
-    testImplementation(MOCKITO_CORE)
+    api(libs.truth)
+    api(libs.kotlinStdlib)
+    testImplementation(libs.truth)
+    testImplementation(libs.mockitoCore)
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(project(":internal-testutils-truth"))
 }
diff --git a/lifecycle/lifecycle-livedata-core/build.gradle b/lifecycle/lifecycle-livedata-core/build.gradle
index e61b4ac..a68baa2 100644
--- a/lifecycle/lifecycle-livedata-core/build.gradle
+++ b/lifecycle/lifecycle-livedata-core/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -33,9 +31,9 @@
     testImplementation(project(":lifecycle:lifecycle-runtime"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
-    testImplementation(KOTLIN_COROUTINES_TEST)
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.kotlinCoroutinesTest)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-livedata-ktx/build.gradle b/lifecycle/lifecycle-livedata-ktx/build.gradle
index de01694..27fbede 100644
--- a/lifecycle/lifecycle-livedata-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-ktx/build.gradle
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -38,19 +35,19 @@
 dependencies {
     api(project(":lifecycle:lifecycle-livedata"))
     api(project(":lifecycle:lifecycle-livedata-core-ktx"))
-    api(KOTLIN_STDLIB)
-    api(KOTLIN_COROUTINES_CORE)
+    api(libs.kotlinStdlib)
+    api(libs.kotlinCoroutinesCore)
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
-    testImplementation(KOTLIN_COROUTINES_TEST)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(KOTLIN_COROUTINES_TEST)
-    androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation(libs.kotlinCoroutinesTest)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.kotlinCoroutinesTest)
+    androidTestImplementation(libs.kotlinCoroutinesAndroid)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-livedata/build.gradle b/lifecycle/lifecycle-livedata/build.gradle
index e936c8a..769df0e 100644
--- a/lifecycle/lifecycle-livedata/build.gradle
+++ b/lifecycle/lifecycle-livedata/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -32,9 +30,9 @@
 
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
-    testImplementation(KOTLIN_COROUTINES_TEST)
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.kotlinCoroutinesTest)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-process/build.gradle b/lifecycle/lifecycle-process/build.gradle
index 06b3c0b..bc8b86f 100644
--- a/lifecycle/lifecycle-process/build.gradle
+++ b/lifecycle/lifecycle-process/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -35,8 +33,8 @@
     api(project(":lifecycle:lifecycle-runtime"))
     api("androidx.startup:startup-runtime:1.0.0")
 
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
index cd16daa..ba2bd36 100644
--- a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
@@ -14,14 +14,10 @@
  * limitations under the License.
  */
 
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
   id("AndroidXPlugin")
@@ -31,12 +27,12 @@
 
 dependencies {
   api(project(":lifecycle:lifecycle-reactivestreams"))
-  api(KOTLIN_STDLIB)
+  api(libs.kotlinStdlib)
 
-  testImplementation(JUNIT)
-  testImplementation(RX_JAVA)
-  testImplementation(TRUTH)
-  testImplementation(KOTLIN_COROUTINES_TEST)
+  testImplementation(libs.junit)
+  testImplementation(libs.rxjava2)
+  testImplementation(libs.truth)
+  testImplementation(libs.kotlinCoroutinesTest)
   testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
   testImplementation("androidx.arch.core:core-testing:2.1.0")
 }
diff --git a/lifecycle/lifecycle-reactivestreams/build.gradle b/lifecycle/lifecycle-reactivestreams/build.gradle
index 6572acf..c301bf7 100644
--- a/lifecycle/lifecycle-reactivestreams/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -31,13 +29,13 @@
     api(project(":lifecycle:lifecycle-livedata"))
     api(project(":lifecycle:lifecycle-runtime"))
     api("androidx.annotation:annotation:1.1.0")
-    api(REACTIVE_STREAMS)
+    api(libs.reactiveStreams)
 
-    annotationProcessor(NULLAWAY)
+    annotationProcessor(libs.nullaway)
 
-    testImplementation(JUNIT)
-    testImplementation(RX_JAVA)
-    testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(libs.junit)
+    testImplementation(libs.rxjava2)
+    testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
 }
diff --git a/lifecycle/lifecycle-runtime-ktx-lint/build.gradle b/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
index 6a17065..524870d 100644
--- a/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
+++ b/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
@@ -18,21 +18,19 @@
 import androidx.build.LibraryType
 import androidx.build.LibraryVersions
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("kotlin")
 }
 
 dependencies {
-    compileOnly(LINT_API_MIN)
-    compileOnly(KOTLIN_STDLIB)
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.kotlinStdlib)
 
-    testImplementation(KOTLIN_STDLIB)
-    testImplementation(LINT_CORE)
-    testImplementation(LINT_TESTS)
-    testImplementation(JUNIT)
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-runtime-ktx/build.gradle b/lifecycle/lifecycle-runtime-ktx/build.gradle
index f638906..e6076bc 100644
--- a/lifecycle/lifecycle-runtime-ktx/build.gradle
+++ b/lifecycle/lifecycle-runtime-ktx/build.gradle
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -30,19 +27,19 @@
 
 dependencies {
     api(project(":lifecycle:lifecycle-runtime"))
-    api(KOTLIN_STDLIB)
-    api(KOTLIN_COROUTINES_ANDROID)
+    api(libs.kotlinStdlib)
+    api(libs.kotlinCoroutinesAndroid)
     api("androidx.annotation:annotation:1.0.0")
 
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
 
     androidTestImplementation(project(":lifecycle:lifecycle-runtime"))
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(KOTLIN_COROUTINES_TEST)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.kotlinCoroutinesTest)
 
     lintPublish(project(":lifecycle:lifecycle-runtime-ktx-lint"))
 }
diff --git a/lifecycle/lifecycle-runtime-testing/build.gradle b/lifecycle/lifecycle-runtime-testing/build.gradle
index ae441d6..3a2a4cd 100644
--- a/lifecycle/lifecycle-runtime-testing/build.gradle
+++ b/lifecycle/lifecycle-runtime-testing/build.gradle
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -30,17 +27,17 @@
 
 dependencies {
     api(project(":lifecycle:lifecycle-runtime-ktx"))
-    api(KOTLIN_STDLIB)
-    api(KOTLIN_COROUTINES_ANDROID)
+    api(libs.kotlinStdlib)
+    api(libs.kotlinCoroutinesAndroid)
 
-    testImplementation(TRUTH)
-    testImplementation(JUNIT)
-    testImplementation(KOTLIN_COROUTINES_TEST)
+    testImplementation(libs.truth)
+    testImplementation(libs.junit)
+    testImplementation(libs.kotlinCoroutinesTest)
 
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-runtime/build.gradle b/lifecycle/lifecycle-runtime/build.gradle
index a053e6d..241d488 100644
--- a/lifecycle/lifecycle-runtime/build.gradle
+++ b/lifecycle/lifecycle-runtime/build.gradle
@@ -1,7 +1,5 @@
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -22,13 +20,13 @@
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.arch.core:core-runtime:2.1.0")
 
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-service/build.gradle b/lifecycle/lifecycle-service/build.gradle
index 2b81a01..9d07cd7 100644
--- a/lifecycle/lifecycle-service/build.gradle
+++ b/lifecycle/lifecycle-service/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -28,9 +26,9 @@
 dependencies {
     api(project(":lifecycle:lifecycle-runtime"))
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
     androidTestImplementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
 }
 
diff --git a/lifecycle/lifecycle-viewmodel-compose/build.gradle b/lifecycle/lifecycle-viewmodel-compose/build.gradle
index 2bdbb1c..5376e2f 100644
--- a/lifecycle/lifecycle-viewmodel-compose/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/build.gradle
@@ -18,9 +18,6 @@
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 import androidx.build.RunApiTasks
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("AndroidXPlugin")
@@ -36,14 +33,14 @@
     api projectOrArtifact(":compose:runtime:runtime")
     api projectOrArtifact(":compose:ui:ui")
 
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
 
     androidTestImplementation projectOrArtifact(":compose:ui:ui-test-junit4")
     androidTestImplementation projectOrArtifact(":compose:test-utils")
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
     androidTestImplementation "androidx.fragment:fragment:1.3.0"
     androidTestImplementation "androidx.appcompat:appcompat:1.3.0-beta01"
     androidTestImplementation projectOrArtifact(":activity:activity-compose")
diff --git a/lifecycle/lifecycle-viewmodel-compose/integration-tests/lifecycle-viewmodel-demos/build.gradle b/lifecycle/lifecycle-viewmodel-compose/integration-tests/lifecycle-viewmodel-demos/build.gradle
index 8979c3c..c41787a 100644
--- a/lifecycle/lifecycle-viewmodel-compose/integration-tests/lifecycle-viewmodel-demos/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/integration-tests/lifecycle-viewmodel-demos/build.gradle
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-
 import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.getKOTLIN_STDLIB
 
 plugins {
     id("AndroidXPlugin")
@@ -29,7 +25,7 @@
 
 dependencies {
     kotlinPlugin projectOrArtifact(":compose:compiler:compiler")
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation projectOrArtifact(":lifecycle:lifecycle-viewmodel-compose")
     implementation projectOrArtifact(
             ":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples"
diff --git a/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle b/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
index 3f1d512..df450b1 100644
--- a/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("AndroidXPlugin")
@@ -31,7 +27,7 @@
 
 dependencies {
     kotlinPlugin projectOrArtifact(":compose:compiler:compiler")
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation projectOrArtifact(":lifecycle:lifecycle-viewmodel-compose")
 }
 
diff --git a/lifecycle/lifecycle-viewmodel-ktx/build.gradle b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
index 854918a..43af991 100644
--- a/lifecycle/lifecycle-viewmodel-ktx/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -27,16 +26,16 @@
 
 dependencies {
     api(project(":lifecycle:lifecycle-viewmodel"))
-    api(KOTLIN_STDLIB)
-    api(KOTLIN_COROUTINES_ANDROID)
+    api(libs.kotlinStdlib)
+    api(libs.kotlinCoroutinesAndroid)
 
-    testImplementation(JUNIT)
-    testImplementation(TRUTH)
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
 
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
index b6496aa..d628653 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -52,12 +50,12 @@
         exclude group: "androidx.lifecycle", module: "lifecycle-viewmodel-savedstate"
         exclude group: "androidx.lifecycle", module: "lifecycle-viewmodel"
     }
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
 }
 
 //used by testImplementation safe-args-generator
diff --git a/lifecycle/lifecycle-viewmodel/build.gradle b/lifecycle/lifecycle-viewmodel/build.gradle
index fb1af37..50abd68 100644
--- a/lifecycle/lifecycle-viewmodel/build.gradle
+++ b/lifecycle/lifecycle-viewmodel/build.gradle
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -35,15 +33,15 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
 
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
 }
 
 androidx {
diff --git a/lint-checks/integration-tests/expected-lint-results.xml b/lint-checks/integration-tests/expected-lint-results.xml
index 6328019..ce6e261 100644
--- a/lint-checks/integration-tests/expected-lint-results.xml
+++ b/lint-checks/integration-tests/expected-lint-results.xml
@@ -148,7 +148,7 @@
     <issue
         id="ClassVerificationFailure"
         severity="Error"
-        message="This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
+        message="This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
         category="Correctness"
         priority="5"
         summary="Even in cases where references to new APIs are gated on SDK_INT checks, run-time class verification will still fail on references to APIs that may not be available at run time, including platform APIs introduced after a library&apos;s minSdkVersion."
@@ -164,7 +164,7 @@
     <issue
         id="ClassVerificationFailure"
         severity="Error"
-        message="This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
+        message="This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
         category="Correctness"
         priority="5"
         summary="Even in cases where references to new APIs are gated on SDK_INT checks, run-time class verification will still fail on references to APIs that may not be available at run time, including platform APIs introduced after a library&apos;s minSdkVersion."
diff --git a/lint-checks/integration-tests/src/main/AndroidManifest.xml b/lint-checks/integration-tests/src/main/AndroidManifest.xml
index 1a39913..98ea13f 100644
--- a/lint-checks/integration-tests/src/main/AndroidManifest.xml
+++ b/lint-checks/integration-tests/src/main/AndroidManifest.xml
@@ -13,4 +13,15 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<manifest package="androidx.lint.integration.tests" />
+<manifest package="androidx.lint.integration.tests"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <application>
+        <!-- Application-level metadata is not allowed. -->
+        <meta-data android:name="name" android:value="value" />
+
+        <service android:name="androidx.core.app.JobIntentService">
+            <!-- Service-level metadata is allowed. -->
+            <meta-data android:name="name" android:value="value" />
+        </service>
+    </application>
+</manifest>
diff --git a/lint-checks/integration-tests/src/main/java/androidx/ConcurrentHashMapUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/ConcurrentHashMapUsageJava.java
new file mode 100644
index 0000000..db30af2
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/ConcurrentHashMapUsageJava.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 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;
+
+import androidx.annotation.NonNull;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@SuppressWarnings("unused")
+public class ConcurrentHashMapUsageJava {
+
+    private final ConcurrentHashMap<?, ?> mMap = new ConcurrentHashMap<>();
+
+    @NonNull
+    public <V, K> Map<V, K> createMap() {
+        return new ConcurrentHashMap<>();
+    }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageJava.java
new file mode 100644
index 0000000..964459f
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageJava.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 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;
+
+import androidx.annotation.Keep;
+
+@Keep
+public class KeepAnnotationUsageJava {
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageJava.java
new file mode 100644
index 0000000..04aafe1
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageJava.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+@SuppressWarnings("unused")
+public class ParcelableUsageJava implements Parcelable {
+
+    protected ParcelableUsageJava(@NonNull Parcel in) {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+    }
+
+    public static final Creator<ParcelableUsageJava> CREATOR = new Creator<ParcelableUsageJava>() {
+        @Override
+        public ParcelableUsageJava createFromParcel(Parcel in) {
+            return new ParcelableUsageJava(in);
+        }
+
+        @Override
+        public ParcelableUsageJava[] newArray(int size) {
+            return new ParcelableUsageJava[size];
+        }
+    };
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/SynchronizedMethodJava.java b/lint-checks/integration-tests/src/main/java/androidx/SynchronizedMethodJava.java
new file mode 100644
index 0000000..73e6365
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/SynchronizedMethodJava.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 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;
+
+@SuppressWarnings("unused")
+public class SynchronizedMethodJava {
+
+    public synchronized void someMethod() {
+    }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageJava.java
new file mode 100644
index 0000000..ff0d71d
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageJava.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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;
+
+import android.annotation.TargetApi;
+
+@SuppressWarnings("unused")
+@TargetApi(29)
+public class TargetApiUsageJava {
+
+    @TargetApi(30)
+    public void someMethod() {
+    }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreator.java b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreator.java
new file mode 100644
index 0000000..8790171
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreator.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2021 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.sample.core.app;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * The goal here is to get common (and correct) behavior around Activity recreation for all API
+ * versions up until P, where the behavior was specified to be useful and implemented to match the
+ * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's
+ * not in the foreground; it will be recreated when the user next interacts with it. This has a few
+ * undesirable consequences:
+ *
+ * <p>1. It's impossible to recreate multiple activities at once, which means that activities in the
+ * background will observe the new configuration before they're recreated. If we keep them on the
+ * old configuration, we have two conflicting configurations active in the app, which leads to
+ * logging skew.
+ *
+ * <p>2. Recreation occurs in the critical path of user interaction - re-inflating a bunch of views
+ * isn't free, and we'd rather do it when we're in the background than when the user is staring at
+ * the screen waiting to see us.
+ *
+ * <p>On API < 26, recreate() was implemented with a single call to a private method on
+ * ActivityThread. That method still exists in 26 and 27, so we can use reflection to call it and
+ * get the exact same behavior as < 26. However, that behavior has problems itself. When
+ * an Activity in the background is recreated, it goes through: destroy -> create -> start ->
+ * resume -> pause and doesn't stop. This is a violation of the contract for onStart/onStop,
+ * but that might be palatable if it didn't also have the effect of preventing new configurations
+ * from being applied - since the Activity doesn't go through onStop, our tracking of whether
+ * our app is visible thinks we're always visible, and thus can't do another recreation later.
+ *
+ * <p>The fix for this is to add the missing onStop() call, by using reflection to call into
+ * ActivityThread.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+@SuppressWarnings({"PrivateApi", "JavaReflectionMemberAccess", "unused"})
+final class ActivityRecreator {
+    private ActivityRecreator() {}
+
+    private static final String LOG_TAG = "ActivityRecreator";
+
+    // Activity.mMainThread
+    protected static final Field mainThreadField;
+    // Activity.mToken. This object is an identifier that is the same between multiple instances of
+    //the same underlying Activity.
+    protected static final Field tokenField;
+    // On API 25, a third param was added to performStopActivity
+    protected static final Method performStopActivity3ParamsMethod;
+    // Before API 25, performStopActivity had two params
+    protected static final Method performStopActivity2ParamsMethod;
+    // ActivityThread.requestRelaunchActivity
+    protected static final Method requestRelaunchActivityMethod;
+
+    private static final Handler mainHandler = new Handler(Looper.getMainLooper());
+
+    static {
+        Class<?> activityThreadClass = getActivityThreadClass();
+        mainThreadField = getMainThreadField();
+        tokenField = getTokenField();
+        performStopActivity3ParamsMethod = getPerformStopActivity3Params(activityThreadClass);
+        performStopActivity2ParamsMethod = getPerformStopActivity2Params(activityThreadClass);
+        requestRelaunchActivityMethod = getRequestRelaunchActivityMethod(activityThreadClass);
+    }
+
+    /**
+     * Equivalent to {@link Activity#recreate}, but working around a number of platform bugs.
+     *
+     * @return true if a recreate() task was successfully scheduled.
+     */
+    static boolean recreate(@NonNull final Activity activity) {
+        // On Android O and later we can rely on the platform recreate()
+        if (SDK_INT >= 28) {
+            activity.recreate();
+            return true;
+        }
+
+        // API 26 needs this workaround but it's not possible because our reflective lookup failed.
+        if (needsRelaunchCall() && requestRelaunchActivityMethod == null) {
+            return false;
+        }
+        // All versions of android so far need this workaround, but it's not possible because our
+        // reflective lookup failed.
+        if (performStopActivity2ParamsMethod == null && performStopActivity3ParamsMethod == null) {
+            return false;
+        }
+        try {
+            final Object token = tokenField.get(activity);
+            if (token == null) {
+                return false;
+            }
+            Object activityThread = mainThreadField.get(activity);
+            if (activityThread == null) {
+                return false;
+            }
+
+            final Application application = activity.getApplication();
+            final LifecycleCheckCallbacks callbacks = new LifecycleCheckCallbacks(activity);
+            application.registerActivityLifecycleCallbacks(callbacks);
+
+            /*
+             * Runnables scheduled before/after recreate() will run before and after the Runnables
+             * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
+             * events that could be caused by recreate() run - that way we can detect onPause()
+             * from the new Activity instance, and schedule onStop to run immediately after it.
+             */
+            mainHandler.post(() -> callbacks.currentlyRecreatingToken = token);
+
+            try {
+                if (needsRelaunchCall()) {
+                    requestRelaunchActivityMethod.invoke(activityThread,
+                            token, null, null, 0, false, null, null, false, false);
+                } else {
+                    activity.recreate();
+                }
+                return true;
+            } finally {
+                mainHandler.post(() -> {
+                    // Since we're calling hidden API, it's entirely possible for it to
+                    // simply do nothing;
+                    // if that's the case, make sure to unregister so we don't leak memory
+                    // waiting for an event that will never happen.
+                    application.unregisterActivityLifecycleCallbacks(callbacks);
+                });
+            }
+        } catch (Throwable t) {
+            return false;
+        }
+    }
+
+    private static final class LifecycleCheckCallbacks implements ActivityLifecycleCallbacks {
+        Object currentlyRecreatingToken;
+
+        private Activity mActivity;
+        private final int mRecreatingHashCode;
+
+        // Whether the activity on which recreate() was called went through onStart after
+        // recreate() was called (and thus the callback was registered).
+        private boolean mStarted = false;
+
+        // Whether the activity on which recreate() was called went through onDestroy after
+        // recreate() was called. This means we successfully initiated a recreate().
+        private boolean mDestroyed = false;
+
+        // Whether we'll force the activity on which recreate() was called to go through an
+        // onStop()
+        private boolean mStopQueued = false;
+
+        LifecycleCheckCallbacks(@NonNull Activity aboutToRecreate) {
+            mActivity = aboutToRecreate;
+            mRecreatingHashCode = mActivity.hashCode();
+        }
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) {
+            // If we see a start call on the original mActivity instance, then the mActivity
+            // starting event executed between our call to recreate() and the actual
+            // recreation of the mActivity. In that case, a stop() call should not be scheduled.
+            if (mActivity == activity) {
+                mStarted = true;
+            }
+        }
+
+        @Override
+        public void onActivityResumed(Activity activity) {
+        }
+
+        @Override
+        public void onActivityPaused(Activity activity) {
+            if (mDestroyed // Original mActivity must be gone
+                    && !mStopQueued // Don't schedule stop twice for one recreate() call
+                    && !mStarted
+                    // Don't schedule stop if the original instance starting raced with recreate()
+                    && queueOnStopIfNecessary(
+                    currentlyRecreatingToken, mRecreatingHashCode, activity)) {
+                mStopQueued = true;
+                // Don't retain this object longer than necessary
+                currentlyRecreatingToken = null;
+            }
+        }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+            // Not possible to get a start/stop pair in the same UI thread loop
+        }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+            if (mActivity == activity) {
+                // Once the original mActivity instance is mDestroyed, we don't need to compare to
+                // it any
+                // longer, and we don't want to retain it any longer than necessary.
+                mActivity = null;
+                mDestroyed = true;
+            }
+        }
+    }
+
+    /**
+     * Returns true if a stop call was scheduled successfully
+     */
+    protected static boolean queueOnStopIfNecessary(
+            Object currentlyRecreatingToken, int currentlyRecreatingHashCode, Activity activity) {
+        try {
+            final Object token = tokenField.get(activity);
+            if (token != currentlyRecreatingToken
+                    || activity.hashCode() != currentlyRecreatingHashCode) {
+                // We're looking at a different activity, don't try to make it stop! Note that
+                // tokens are reused on SDK 21-23 but Activity objects (and thus hashCode, in
+                // all likelihood) are not, so we need to check both.
+                return false;
+            }
+            final Object activityThread = mainThreadField.get(activity);
+            // These operations are posted at the front of the queue, so that operations
+            // scheduled from onCreate, onStart etc run after the onStop call - this should
+            // cause any redundant loads to be immediately cancelled.
+            mainHandler.postAtFrontOfQueue(() -> {
+                try {
+                    if (performStopActivity3ParamsMethod != null) {
+                        performStopActivity3ParamsMethod.invoke(activityThread,
+                                token, false, "AppCompat recreation");
+                    } else {
+                        performStopActivity2ParamsMethod.invoke(activityThread,
+                                token, false);
+                    }
+                } catch (RuntimeException e) {
+                    // If an Activity throws from onStop, don't swallow it
+                    if (e.getClass() == RuntimeException.class
+                            && e.getMessage() != null
+                            && e.getMessage().startsWith("Unable to stop")) {
+                        throw e;
+                    }
+                    // Otherwise just swallow it - we're calling random private methods,
+                    // there's no guarantee on how they'll behave.
+                } catch (Throwable t) {
+                    Log.e(LOG_TAG, "Exception while invoking performStopActivity", t);
+                }
+            });
+            return true;
+        } catch (Throwable t) {
+            Log.e(LOG_TAG, "Exception while fetching field values", t);
+            return false;
+        }
+    }
+
+    private static Method getPerformStopActivity3Params(Class<?> activityThreadClass) {
+        if (activityThreadClass == null) {
+            return null;
+        }
+        try {
+            Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+                    IBinder.class, boolean.class, String.class);
+            performStop.setAccessible(true);
+            return performStop;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Method getPerformStopActivity2Params(Class<?> activityThreadClass) {
+        if (activityThreadClass == null) {
+            return null;
+        }
+        try {
+            Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+                    IBinder.class, boolean.class);
+            performStop.setAccessible(true);
+            return performStop;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static boolean needsRelaunchCall() {
+        return SDK_INT == 26 || SDK_INT == 27;
+    }
+
+    private static Method getRequestRelaunchActivityMethod(Class<?> activityThreadClass) {
+        if (!needsRelaunchCall() || activityThreadClass == null) {
+            return null;
+        }
+        try {
+            Method relaunch = activityThreadClass.getDeclaredMethod(
+                    "requestRelaunchActivity",
+                    IBinder.class,
+                    List.class,
+                    List.class,
+                    int.class,
+                    boolean.class,
+                    Configuration.class,
+                    Configuration.class,
+                    boolean.class,
+                    boolean.class);
+            relaunch.setAccessible(true);
+            return relaunch;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Field getMainThreadField() {
+        try {
+            Field mainThreadField = Activity.class.getDeclaredField("mMainThread");
+            mainThreadField.setAccessible(true);
+            return mainThreadField;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Field getTokenField() {
+        try {
+            Field tokenField = Activity.class.getDeclaredField("mToken");
+            tokenField.setAccessible(true);
+            return tokenField;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Class<?> getActivityThreadClass() {
+        try {
+            return Class.forName("android.app.ActivityThread");
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorChecked.java b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorChecked.java
new file mode 100644
index 0000000..0523d2d
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorChecked.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2021 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.sample.core.app;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.ChecksSdkIntAtLeast;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * The goal here is to get common (and correct) behavior around Activity recreation for all API
+ * versions up until P, where the behavior was specified to be useful and implemented to match the
+ * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's
+ * not in the foreground; it will be recreated when the user next interacts with it. This has a few
+ * undesirable consequences:
+ *
+ * <p>1. It's impossible to recreate multiple activities at once, which means that activities in the
+ * background will observe the new configuration before they're recreated. If we keep them on the
+ * old configuration, we have two conflicting configurations active in the app, which leads to
+ * logging skew.
+ *
+ * <p>2. Recreation occurs in the critical path of user interaction - re-inflating a bunch of views
+ * isn't free, and we'd rather do it when we're in the background than when the user is staring at
+ * the screen waiting to see us.
+ *
+ * <p>On API < 26, recreate() was implemented with a single call to a private method on
+ * ActivityThread. That method still exists in 26 and 27, so we can use reflection to call it and
+ * get the exact same behavior as < 26. However, that behavior has problems itself. When
+ * an Activity in the background is recreated, it goes through: destroy -> create -> start ->
+ * resume -> pause and doesn't stop. This is a violation of the contract for onStart/onStop,
+ * but that might be palatable if it didn't also have the effect of preventing new configurations
+ * from being applied - since the Activity doesn't go through onStop, our tracking of whether
+ * our app is visible thinks we're always visible, and thus can't do another recreation later.
+ *
+ * <p>The fix for this is to add the missing onStop() call, by using reflection to call into
+ * ActivityThread.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+@SuppressWarnings({"PrivateApi", "JavaReflectionMemberAccess", "unused"})
+final class ActivityRecreatorChecked {
+    private ActivityRecreatorChecked() {}
+
+    private static final String LOG_TAG = "ActivityRecreatorChecked";
+
+    // Activity.mMainThread
+    protected static final Field mainThreadField;
+    // Activity.mToken. This object is an identifier that is the same between multiple instances of
+    //the same underlying Activity.
+    protected static final Field tokenField;
+    // On API 25, a third param was added to performStopActivity
+    protected static final Method performStopActivity3ParamsMethod;
+    // Before API 25, performStopActivity had two params
+    protected static final Method performStopActivity2ParamsMethod;
+    // ActivityThread.requestRelaunchActivity
+    protected static final Method requestRelaunchActivityMethod;
+
+    private static final Handler mainHandler = new Handler(Looper.getMainLooper());
+
+    static {
+        Class<?> activityThreadClass = getActivityThreadClass();
+        mainThreadField = getMainThreadField();
+        tokenField = getTokenField();
+        performStopActivity3ParamsMethod = getPerformStopActivity3Params(activityThreadClass);
+        performStopActivity2ParamsMethod = getPerformStopActivity2Params(activityThreadClass);
+        requestRelaunchActivityMethod = getRequestRelaunchActivityMethod(activityThreadClass);
+    }
+
+    /**
+     * Equivalent to {@link Activity#recreate}, but working around a number of platform bugs.
+     *
+     * @return true if a recreate() task was successfully scheduled.
+     */
+    static boolean recreate(@NonNull final Activity activity) {
+        // On Android O and later we can rely on the platform recreate()
+        if (SDK_INT >= 28) {
+            activity.recreate();
+            return true;
+        }
+
+        // API 26 needs this workaround but it's not possible because our reflective lookup failed.
+        if (needsRelaunchCall() && requestRelaunchActivityMethod == null) {
+            return false;
+        }
+        // All versions of android so far need this workaround, but it's not possible because our
+        // reflective lookup failed.
+        if (performStopActivity2ParamsMethod == null && performStopActivity3ParamsMethod == null) {
+            return false;
+        }
+        try {
+            final Object token = tokenField.get(activity);
+            if (token == null) {
+                return false;
+            }
+            Object activityThread = mainThreadField.get(activity);
+            if (activityThread == null) {
+                return false;
+            }
+
+            final Application application = activity.getApplication();
+            final LifecycleCheckCallbacks callbacks = new LifecycleCheckCallbacks(activity);
+            application.registerActivityLifecycleCallbacks(callbacks);
+
+            /*
+             * Runnables scheduled before/after recreate() will run before and after the Runnables
+             * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
+             * events that could be caused by recreate() run - that way we can detect onPause()
+             * from the new Activity instance, and schedule onStop to run immediately after it.
+             */
+            mainHandler.post(() -> callbacks.currentlyRecreatingToken = token);
+
+            try {
+                if (needsRelaunchCall()) {
+                    requestRelaunchActivityMethod.invoke(activityThread,
+                            token, null, null, 0, false, null, null, false, false);
+                } else {
+                    activity.recreate();
+                }
+                return true;
+            } finally {
+                mainHandler.post(() -> {
+                    // Since we're calling hidden API, it's entirely possible for it to
+                    // simply do nothing;
+                    // if that's the case, make sure to unregister so we don't leak memory
+                    // waiting for an event that will never happen.
+                    application.unregisterActivityLifecycleCallbacks(callbacks);
+                });
+            }
+        } catch (Throwable t) {
+            return false;
+        }
+    }
+
+    // Only reachable on SDK_INT < 28
+    private static final class LifecycleCheckCallbacks implements ActivityLifecycleCallbacks {
+        Object currentlyRecreatingToken;
+
+        private Activity mActivity;
+        private final int mRecreatingHashCode;
+
+        // Whether the activity on which recreate() was called went through onStart after
+        // recreate() was called (and thus the callback was registered).
+        private boolean mStarted = false;
+
+        // Whether the activity on which recreate() was called went through onDestroy after
+        // recreate() was called. This means we successfully initiated a recreate().
+        private boolean mDestroyed = false;
+
+        // Whether we'll force the activity on which recreate() was called to go through an
+        // onStop()
+        private boolean mStopQueued = false;
+
+        LifecycleCheckCallbacks(@NonNull Activity aboutToRecreate) {
+            mActivity = aboutToRecreate;
+            mRecreatingHashCode = mActivity.hashCode();
+        }
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) {
+            // If we see a start call on the original mActivity instance, then the mActivity
+            // starting event executed between our call to recreate() and the actual
+            // recreation of the mActivity. In that case, a stop() call should not be scheduled.
+            if (mActivity == activity) {
+                mStarted = true;
+            }
+        }
+
+        @Override
+        public void onActivityResumed(Activity activity) {
+        }
+
+        @Override
+        public void onActivityPaused(Activity activity) {
+            if (mDestroyed // Original mActivity must be gone
+                    && !mStopQueued // Don't schedule stop twice for one recreate() call
+                    && !mStarted
+                    // Don't schedule stop if the original instance starting raced with recreate()
+                    && queueOnStopIfNecessary(
+                    currentlyRecreatingToken, mRecreatingHashCode, activity)) {
+                mStopQueued = true;
+                // Don't retain this object longer than necessary
+                currentlyRecreatingToken = null;
+            }
+        }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+            // Not possible to get a start/stop pair in the same UI thread loop
+        }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+            if (mActivity == activity) {
+                // Once the original mActivity instance is mDestroyed, we don't need to compare to
+                // it any
+                // longer, and we don't want to retain it any longer than necessary.
+                mActivity = null;
+                mDestroyed = true;
+            }
+        }
+    }
+
+    /**
+     * Returns true if a stop call was scheduled successfully.
+     *
+     * Only reachable on SDK < 28.
+     */
+    protected static boolean queueOnStopIfNecessary(
+            Object currentlyRecreatingToken, int currentlyRecreatingHashCode, Activity activity) {
+        try {
+            final Object token = tokenField.get(activity);
+            if (token != currentlyRecreatingToken
+                    || activity.hashCode() != currentlyRecreatingHashCode) {
+                // We're looking at a different activity, don't try to make it stop! Note that
+                // tokens are reused on SDK 21-23 but Activity objects (and thus hashCode, in
+                // all likelihood) are not, so we need to check both.
+                return false;
+            }
+            final Object activityThread = mainThreadField.get(activity);
+            // These operations are posted at the front of the queue, so that operations
+            // scheduled from onCreate, onStart etc run after the onStop call - this should
+            // cause any redundant loads to be immediately cancelled.
+            mainHandler.postAtFrontOfQueue(() -> {
+                try {
+                    if (SDK_INT < 28) {
+                        if (performStopActivity3ParamsMethod != null) {
+                            performStopActivity3ParamsMethod.invoke(activityThread,
+                                    token, false, "AppCompat recreation");
+                        } else {
+                            performStopActivity2ParamsMethod.invoke(activityThread,
+                                    token, false);
+                        }
+                    }
+                } catch (RuntimeException e) {
+                    // If an Activity throws from onStop, don't swallow it
+                    if (e.getClass() == RuntimeException.class
+                            && e.getMessage() != null
+                            && e.getMessage().startsWith("Unable to stop")) {
+                        throw e;
+                    }
+                    // Otherwise just swallow it - we're calling random private methods,
+                    // there's no guarantee on how they'll behave.
+                } catch (Throwable t) {
+                    Log.e(LOG_TAG, "Exception while invoking performStopActivity", t);
+                }
+            });
+            return true;
+        } catch (Throwable t) {
+            Log.e(LOG_TAG, "Exception while fetching field values", t);
+            return false;
+        }
+    }
+
+    private static Method getPerformStopActivity3Params(Class<?> activityThreadClass) {
+        if (activityThreadClass == null) {
+            return null;
+        }
+        try {
+            Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+                    IBinder.class, boolean.class, String.class);
+            performStop.setAccessible(true);
+            return performStop;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Method getPerformStopActivity2Params(Class<?> activityThreadClass) {
+        if (activityThreadClass == null) {
+            return null;
+        }
+        try {
+            Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+                    IBinder.class, boolean.class);
+            performStop.setAccessible(true);
+            return performStop;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    @ChecksSdkIntAtLeast(api = 26)
+    private static boolean needsRelaunchCall() {
+        return SDK_INT == 26 || SDK_INT == 27;
+    }
+
+    private static Method getRequestRelaunchActivityMethod(Class<?> activityThreadClass) {
+        if (!needsRelaunchCall() || activityThreadClass == null) {
+            return null;
+        }
+        try {
+            Method relaunch = activityThreadClass.getDeclaredMethod(
+                    "requestRelaunchActivity",
+                    IBinder.class,
+                    List.class,
+                    List.class,
+                    int.class,
+                    boolean.class,
+                    Configuration.class,
+                    Configuration.class,
+                    boolean.class,
+                    boolean.class);
+            relaunch.setAccessible(true);
+            return relaunch;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Field getMainThreadField() {
+        try {
+            Field mainThreadField = Activity.class.getDeclaredField("mMainThread");
+            mainThreadField.setAccessible(true);
+            return mainThreadField;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Field getTokenField() {
+        try {
+            Field tokenField = Activity.class.getDeclaredField("mToken");
+            tokenField.setAccessible(true);
+            return tokenField;
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    private static Class<?> getActivityThreadClass() {
+        try {
+            return Class.forName("android.app.ActivityThread");
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/core/widget/ListViewCompat.java b/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompat.java
similarity index 97%
rename from lint-checks/integration-tests/src/main/java/androidx/core/widget/ListViewCompat.java
rename to lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompat.java
index 1d51e41..216ef20 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/core/widget/ListViewCompat.java
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompat.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.core.widget;
+package androidx.sample.core.widget;
 
 import android.os.Build;
 import android.view.View;
diff --git a/lint-checks/lint-baseline.xml b/lint-checks/lint-baseline.xml
deleted file mode 100644
index 42a176b..0000000
--- a/lint-checks/lint-baseline.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
-
-</issues>
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 5bc2e75..4ee07b7 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
@@ -22,13 +22,14 @@
 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.Incident
 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.intellij.psi.PsiCompiledElement
 import org.jetbrains.uast.UAnnotated
 import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UClass
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.resolveToUElement
 
@@ -45,11 +46,28 @@
 
     private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
         override fun visitAnnotation(node: UAnnotation) {
+            if (DEBUG) {
+                if (APPLICABLE_ANNOTATIONS.contains(node.qualifiedName) && node.sourcePsi != null) {
+                    (node.uastParent as? UClass)?.let { annotation ->
+                        println(
+                            "${context.driver.mode}: declared ${annotation.qualifiedName} in " +
+                                "${context.project}"
+                        )
+                    }
+                }
+            }
+
+            // If we find an usage of an experimentally-declared annotation, check it.
             val annotation = node.resolveToUElement()
             if (annotation is UAnnotated) {
                 val annotations = context.evaluator.getAllAnnotations(annotation, false)
-                val isOptIn = annotations.any { APPLICABLE_ANNOTATIONS.contains(it.qualifiedName) }
-                if (isOptIn) {
+                if (annotations.any { APPLICABLE_ANNOTATIONS.contains(it.qualifiedName) }) {
+                    if (DEBUG) {
+                        println(
+                            "${context.driver.mode}: used ${node.qualifiedName} in " +
+                                "${context.project}"
+                        )
+                    }
                     verifyUsageOfElementIsWithinSameGroup(context, node, annotation, ISSUE)
                 }
             }
@@ -64,28 +82,28 @@
     ) {
         val evaluator = context.evaluator
         val usageCoordinates = evaluator.getLibrary(usage) ?: context.project.mavenCoordinate
-        val annotationCoordinates = evaluator.getLibrary(annotation) ?: run {
-            // Is the annotation defined in source code?
-            if (usageCoordinates != null && annotation !is PsiCompiledElement) {
-                annotation.sourcePsi?.let { sourcePsi ->
-                    evaluator.getProject(sourcePsi)?.mavenCoordinate
-                }
-            } else {
-                null
-            }
-        }
         val usageGroupId = usageCoordinates?.groupId
-        val annotationGroupId = annotationCoordinates?.groupId
+        val annotationGroupId = evaluator.getLibrary(annotation)?.groupId
         if (annotationGroupId != usageGroupId && annotationGroupId != null) {
-            context.report(
-                issue, usage, context.getNameLocation(usage),
-                "`Experimental` and `RequiresOptIn` APIs may only be used within the same-version" +
-                    " group where they were defined."
-            )
+            if (DEBUG) {
+                println(
+                    "${context.driver.mode}: report usage of $annotationGroupId in $usageGroupId"
+                )
+            }
+            Incident(context)
+                .issue(issue)
+                .at(usage)
+                .message(
+                    "`Experimental` and `RequiresOptIn` APIs may only be used within the " +
+                        "same-version group where they were defined."
+                )
+                .report()
         }
     }
 
     companion object {
+        private const val DEBUG = false
+
         private const val KOTLIN_EXPERIMENTAL_ANNOTATION = "kotlin.Experimental"
         private const val KOTLIN_REQUIRES_OPT_IN_ANNOTATION = "kotlin.RequiresOptIn"
         private const val JAVA_EXPERIMENTAL_ANNOTATION =
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt b/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
index 32c2133..725f322c 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
@@ -16,6 +16,7 @@
 @file:Suppress("UnstableApiUsage")
 
 package androidx.build.lint
+
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Implementation
@@ -23,47 +24,55 @@
 import com.android.tools.lint.detector.api.JavaContext
 import com.android.tools.lint.detector.api.Scope
 import com.android.tools.lint.checks.VersionChecks.Companion.isWithinVersionCheckConditional
-import com.android.sdklib.SdkVersionInfo
+import com.android.sdklib.SdkVersionInfo.HIGHEST_KNOWN_API
 import com.android.tools.lint.detector.api.Severity
 import com.android.tools.lint.detector.api.SourceCodeScanner
 import com.intellij.psi.PsiMethod
 import org.jetbrains.uast.UCallExpression
-const val METHOD_REFLECTION_CLASS = "java.lang.reflect.Method"
-class BanUncheckedReflection : Detector(), SourceCodeScanner {
-    override fun getApplicableMethodNames() = listOf("invoke")
-    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
-        // We are not really monitoring if the reflection call is within the right API check
-        // we leave that to the user, and so we check for any API check really. That means
-        // any check with an upper bound of the highest known API or a with a lower bound of 1
-        // (which should technically include every check) is good enough.
-        // Return if not reflection
-        if (!context.evaluator.isMemberInClass(method, METHOD_REFLECTION_CLASS)) return
-        // If not within an SDK check, flag
-        if (!isWithinVersionCheckConditional(
-                context, node, SdkVersionInfo.HIGHEST_KNOWN_API, false
-            ) && !isWithinVersionCheckConditional(
-                    context, node, 1, true
-                )
-        ) {
 
+class BanUncheckedReflection : Detector(), SourceCodeScanner {
+
+    override fun getApplicableMethodNames() = listOf(
+        METHOD_INVOKE_NAME
+    )
+
+    override fun visitMethodCall(
+        context: JavaContext,
+        node: UCallExpression,
+        method: PsiMethod
+    ) {
+        // We don't care if the invocation is correct -- there's another lint for that. We're
+        // just enforcing the "all reflection on the platform SDK must be gated on SDK_INT checks"
+        // policy. Also -- since we're not actually checking whether the invocation is on the
+        // platform SDK -- we're discouraging reflection in general.
+
+        // Skip if this isn't a call to `Method.invoke`.
+        if (!context.evaluator.isMemberInClass(method, METHOD_REFLECTION_CLASS)) return
+
+        // Flag if the call isn't inside an SDK_INT check.
+        if (!isWithinVersionCheckConditional(context, node, HIGHEST_KNOWN_API, false) &&
+            !isWithinVersionCheckConditional(context, node, 1, true)
+        ) {
             context.report(
                 ISSUE, node, context.getLocation(node),
-                "Calling Method.invoke without an SDK check"
+                "Calling `Method.invoke` without an SDK check"
             )
         }
     }
+
     companion object {
         val ISSUE = Issue.create(
             "BanUncheckedReflection",
             "Reflection that is not within an SDK check",
-            "Use of reflection can be risky and there is never a" +
-                " reason to use reflection without" +
-                " having to check for the device's SDK (either through SDK_INT comparison or " +
-                "methods such as isAtLeastP etc...)" +
-                ". Please surround the Method.invoke" +
-                " call with the appropriate SDK_INT check.",
+            "Jetpack policy discourages reflection. In cases where reflection is used on " +
+                "platform SDK classes, it must be used within an `SDK_INT` check that delegates " +
+                "to an equivalent public API on the latest version of the platform. If no " +
+                "equivalent public API exists, reflection must not be used.",
             Category.CORRECTNESS, 5, Severity.ERROR,
             Implementation(BanUncheckedReflection::class.java, Scope.JAVA_FILE_SCOPE)
         )
+
+        const val METHOD_REFLECTION_CLASS = "java.lang.reflect.Method"
+        const val METHOD_INVOKE_NAME = "invoke"
     }
 }
diff --git a/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt
new file mode 100644
index 0000000..8bcfd3e
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.ProjectDescription
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.checks.infrastructure.TestMode
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import java.io.FileNotFoundException
+
+/**
+ * Implementation of [LintDetectorTest] that's slightly more Kotlin-friendly.
+ */
+abstract class AbstractLintDetectorTest(
+    val useDetector: Detector,
+    val useIssues: List<Issue>,
+    val stubs: Array<TestFile> = emptyArray(),
+) : LintDetectorTest() {
+    override fun getDetector(): Detector = useDetector
+
+    override fun getIssues(): List<Issue> = useIssues
+
+    fun check(
+        vararg projects: ProjectDescription
+    ): TestLintResult {
+        // If we have stubs, push those into a virtual project and pass them through the call to
+        // projects(), since attempting to call files() would overwrite the call to projects().
+        val projectsWithStubs = if (stubs.isNotEmpty()) {
+            arrayOf(*projects, project().files(*stubs))
+        } else {
+            projects
+        }
+
+        return lint()
+            .projects(*projectsWithStubs).testModes(TestMode.DEFAULT, TestMode.PARTIAL)
+            .run()
+    }
+
+    fun check(
+        vararg files: TestFile,
+    ): TestLintResult {
+        return lint()
+            .files(
+                *stubs,
+                *files
+            )
+            .run()
+    }
+}
+
+/**
+ * Creates a new [ProjectDescription].
+ */
+fun project(): ProjectDescription = ProjectDescription()
+
+/**
+ * Loads a [TestFile] from `AndroidManifest.xml` included in the JAR resources.
+ */
+fun manifestSample(): TestFile = TestFiles.manifest(
+    Stubs::class.java.getResource(
+        "/AndroidManifest.xml"
+    )?.readText() ?: throw FileNotFoundException(
+        "Could not find AndroidManifest.xml in the integration test project"
+    )
+)
+
+/**
+ * Loads a [TestFile] from Java source code included in the JAR resources.
+ */
+fun javaSample(className: String): TestFile = TestFiles.java(
+    Stubs::class.java.getResource(
+        "/java/${className.replace('.', '/')}.java"
+    )?.readText() ?: throw FileNotFoundException(
+        "Could not find Java sources for $className in the integration test project"
+    )
+)
+
+/**
+ * Loads a [TestFile] from Kotlin source code included in the JAR resources.
+ */
+fun ktSample(className: String): TestFile = TestFiles.kotlin(
+    Stubs::class.java.getResource(
+        "/java/${className.replace('.', '/')}.kt"
+    )?.readText() ?: throw FileNotFoundException(
+        "Could not find Kotlin sources for $className in the integration test project"
+    )
+)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt
new file mode 100644
index 0000000..a01e86e
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanConcurrentHashMapTest : AbstractLintDetectorTest(
+    useDetector = BanConcurrentHashMap(),
+    useIssues = listOf(BanConcurrentHashMap.ISSUE),
+) {
+
+    @Test
+    fun `Detection of ConcurrentHashMap usage in Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.ConcurrentHashMapUsageJava"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/androidx/ConcurrentHashMapUsageJava.java:22: Error: Detected ConcurrentHashMap usage. [BanConcurrentHashMap]
+import java.util.concurrent.ConcurrentHashMap;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+}
\ No newline at end of file
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 0c7e01e..64e10d8 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
@@ -14,20 +14,22 @@
  * limitations under the License.
  */
 
-@file:Suppress("UnstableApiUsage")
+@file:Suppress("UnstableApiUsage", "GroovyUnusedAssignment")
 
 package androidx.build.lint
 
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-import com.android.tools.lint.checks.infrastructure.TestFiles.gradle
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import com.android.tools.lint.checks.infrastructure.ProjectDescription
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class BanInappropriateExperimentalUsageTest {
+class BanInappropriateExperimentalUsageTest : AbstractLintDetectorTest(
+    useDetector = BanInappropriateExperimentalUsage(),
+    useIssues = listOf(BanInappropriateExperimentalUsage.ISSUE),
+    stubs = arrayOf(Stubs.OptIn),
+) {
 
     @Test
     fun `Test within-module Experimental usage via Gradle model`() {
@@ -42,24 +44,23 @@
                     group=sample.annotation.provider
                     """
                 ).indented(),
-                OPT_IN_KT,
             )
 
-        lint()
-            .projects(provider)
-            .issues(BanInappropriateExperimentalUsage.ISSUE)
-            .run()
-            .expect(
-                """
-                No warnings.
-                """.trimIndent()
-            )
+        /* ktlint-disable max-line-length */
+        val expected = """
+No warnings.
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(provider).expect(expected)
     }
 
+    @Ignore("b/188814760")
     @Test
     fun `Test cross-module Experimental usage via Gradle model`() {
         val provider = project()
             .name("provider")
+            .type(ProjectDescription.Type.LIBRARY)
             .report(false)
             .files(
                 ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
@@ -70,11 +71,11 @@
                     group=sample.annotation.provider
                     """
                 ).indented(),
-                OPT_IN_KT,
             )
 
         val consumer = project()
             .name("consumer")
+            .type(ProjectDescription.Type.LIBRARY)
             .dependsOn(provider)
             .files(
                 ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
@@ -83,69 +84,18 @@
                     apply plugin: 'com.android.library'
                     group=androidx.sample.consumer
                     """
-                ).indented()
+                ).indented(),
             )
 
-        lint()
-            .projects(provider, consumer)
-            .issues(BanInappropriateExperimentalUsage.ISSUE)
-            .run()
-            .expect(
-                /* ktlint-enable max-line-length */
-                """
-                src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:25: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
-                    @ExperimentalSampleAnnotationJava
-                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-                1 errors, 0 warnings
-                """.trimIndent()
-                /* ktlint-enable max-line-length */
-            )
+        /* ktlint-disable max-line-length */
+        val expected = """
+../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:25: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
+    @ExperimentalSampleAnnotationJava
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(provider, consumer).expect(expected)
     }
 }
-
-/* ktlint-disable max-line-length */
-
-/**
- * [TestFile] containing OptIn.kt from the Kotlin standard library.
- *
- * This is a workaround for the Kotlin standard library used by the Lint test harness not
- * including the Experimental annotation by default.
- */
-private val OPT_IN_KT: TestFile = TestFiles.kotlin(
-    """
-    package kotlin
-
-    import kotlin.annotation.AnnotationRetention.BINARY
-    import kotlin.annotation.AnnotationRetention.SOURCE
-    import kotlin.annotation.AnnotationTarget.*
-    import kotlin.internal.RequireKotlin
-    import kotlin.internal.RequireKotlinVersionKind
-    import kotlin.reflect.KClass
-
-    @Target(ANNOTATION_CLASS)
-    @Retention(BINARY)
-    @SinceKotlin("1.3")
-    @RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
-    public annotation class RequiresOptIn(
-        val message: String = "",
-        val level: Level = Level.ERROR
-    ) {
-        public enum class Level {
-            WARNING,
-            ERROR,
-        }
-    }
-
-    @Target(
-        CLASS, PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, EXPRESSION, FILE, TYPEALIAS
-    )
-    @Retention(SOURCE)
-    @SinceKotlin("1.3")
-    @RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
-    public annotation class OptIn(
-        vararg val markerClass: KClass<out Annotation>
-    )
-    """.trimIndent()
-)
-
-/* ktlint-enable max-line-length */
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
index 4b5b504..dceb43b 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
@@ -14,55 +14,36 @@
  * limitations under the License.
  */
 
+@file:Suppress("UnstableApiUsage")
+
 package androidx.build.lint
 
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-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 com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Issue
-
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
 
-class BanKeepAnnotationTest : LintDetectorTest() {
-    override fun getDetector(): Detector = BanKeepAnnotation()
+@RunWith(JUnit4::class)
+class BanKeepAnnotationTest : AbstractLintDetectorTest(
+    useDetector = BanKeepAnnotation(),
+    useIssues = listOf(BanKeepAnnotation.ISSUE),
+    stubs = arrayOf(Stubs.Keep),
+) {
 
-    override fun getIssues(): List<Issue> = listOf(
-        BanKeepAnnotation.ISSUE
-    )
-
-    private fun check(code: String): TestLintResult {
-        return lint().files(
-            java(annotationSource),
-            java(code)
+    @Test
+    fun `Detection of Keep annotation in Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.KeepAnnotationUsageJava"),
         )
-            .run()
-    }
 
-    private val annotationSource = """
-        package androidx.annotation;
-
-        public @interface Keep {
-        }
-    """
-
-    @Test fun testAnnotatedUnreferencedClass() {
-        val input = """
-            package androidx.sample;
-
-            import androidx.annotation.Keep;
-            @Keep
-            public class SampleClass {
-            }
-        """
+        /* ktlint-disable max-line-length */
         val expected = """
-            src/androidx/sample/SampleClass.java:4: Error: Uses @Keep annotation [BanKeepAnnotation]
-            @Keep
-            ~~~~~
-            1 errors, 0 warnings
-        """
-        check(input.trimIndent())
-            .expect(expected.trimIndent())
+src/androidx/KeepAnnotationUsageJava.java:21: Error: Uses @Keep annotation [BanKeepAnnotation]
+@Keep
+~~~~~
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
     }
 }
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt
new file mode 100644
index 0000000..b2cc7d4
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanParcelableUsageTest : AbstractLintDetectorTest(
+    useDetector = BanParcelableUsage(),
+    useIssues = listOf(BanParcelableUsage.ISSUE),
+) {
+
+    @Test
+    fun `Detection of Parcelable usage in Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.ParcelableUsageJava"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/androidx/ParcelableUsageJava.java:25: Error: Class implements android.os.Parcelable [BanParcelableUsage]
+public class ParcelableUsageJava implements Parcelable {
+             ~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+}
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt
new file mode 100644
index 0000000..702b681
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanSynchronizedMethodsTest : AbstractLintDetectorTest(
+    useDetector = BanSynchronizedMethods(),
+    useIssues = listOf(BanSynchronizedMethods.ISSUE),
+) {
+
+    @Test
+    fun `Detection of synchronized methods in Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.SynchronizedMethodJava"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/androidx/SynchronizedMethodJava.java:22: Error: Use of synchronized methods is not recommended [BanSynchronizedMethods]
+    public synchronized void someMethod() {
+    ^
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt
new file mode 100644
index 0000000..49429e0
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanTargetApiAnnotationTest : AbstractLintDetectorTest(
+    useDetector = BanTargetApiAnnotation(),
+    useIssues = listOf(BanTargetApiAnnotation.ISSUE),
+) {
+
+    @Test
+    fun `Detection of TargetApi usage in Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.TargetApiUsageJava"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/androidx/TargetApiUsageJava.java:22: Error: Uses @TargetApi annotation [BanTargetApiAnnotation]
+@TargetApi(29)
+~~~~~~~~~~~~~~
+src/androidx/TargetApiUsageJava.java:25: Error: Uses @TargetApi annotation [BanTargetApiAnnotation]
+    @TargetApi(30)
+    ~~~~~~~~~~~~~~
+2 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+}
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt
new file mode 100644
index 0000000..6c66b16
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanUncheckedReflectionTest : AbstractLintDetectorTest(
+    useDetector = BanUncheckedReflection(),
+    useIssues = listOf(BanUncheckedReflection.ISSUE),
+    stubs = arrayOf(Stubs.ChecksSdkIntAtLeast),
+) {
+
+    @Test
+    fun `Detection of unchecked reflection in real-world Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.sample.core.app.ActivityRecreator"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/androidx/sample/core/app/ActivityRecreator.java:145: Error: Calling Method.invoke without an SDK check [BanUncheckedReflection]
+                    requestRelaunchActivityMethod.invoke(activityThread,
+                    ^
+src/androidx/sample/core/app/ActivityRecreator.java:262: Error: Calling Method.invoke without an SDK check [BanUncheckedReflection]
+                        performStopActivity3ParamsMethod.invoke(activityThread,
+                        ^
+src/androidx/sample/core/app/ActivityRecreator.java:265: Error: Calling Method.invoke without an SDK check [BanUncheckedReflection]
+                        performStopActivity2ParamsMethod.invoke(activityThread,
+                        ^
+3 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+
+    @Test
+    fun `Checked reflection in real-world Java sources`() {
+        val input = arrayOf(
+            javaSample("androidx.sample.core.app.ActivityRecreatorChecked"),
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+No warnings.
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
index 253df56..c1006d3 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
@@ -14,38 +14,28 @@
  * limitations under the License.
  */
 
+@file:Suppress("UnstableApiUsage")
+
 package androidx.build.lint
 
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.manifest
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-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
 
-@Suppress("UnstableApiUsage")
 @RunWith(JUnit4::class)
-class ClassVerificationFailureDetectorTest {
-
-    private fun check(
-        vararg testFiles: TestFile,
-        minSdkVersion: Int = 14,
-    ): TestLintResult {
-        return lint()
-            .files(
-                manifest().minSdk(minSdkVersion),
-                *testFiles,
-            )
-            .issues(ClassVerificationFailureDetector.ISSUE)
-            .run()
-    }
+class ClassVerificationFailureDetectorTest : AbstractLintDetectorTest(
+    useDetector = ClassVerificationFailureDetector(),
+    useIssues = listOf(ClassVerificationFailureDetector.ISSUE),
+    stubs = arrayOf(
+        // AndroidManifest with minSdkVersion=14
+        manifest().minSdk(14),
+    ),
+) {
 
     @Test
     fun `Detection of unsafe references in Java sources`() {
         val input = arrayOf(
-            javaSample("androidx.ClassVerificationFailureFromJava")
+            javaSample("androidx.ClassVerificationFailureFromJava"),
         )
 
         /* ktlint-disable max-line-length */
@@ -72,22 +62,22 @@
     @Test
     fun `Detection and auto-fix of unsafe references in real-world Java sources`() {
         val input = arrayOf(
-            javaSample("androidx.core.widget.ListViewCompat")
+            javaSample("androidx.sample.core.widget.ListViewCompat"),
         )
 
         /* ktlint-disable max-line-length */
         val expected = """
-src/androidx/core/widget/ListViewCompat.java:39: Error: This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
+src/androidx/sample/core/widget/ListViewCompat.java:39: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
             listView.scrollListBy(y);
                      ~~~~~~~~~~~~
-src/androidx/core/widget/ListViewCompat.java:69: Error: This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
+src/androidx/sample/core/widget/ListViewCompat.java:69: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
             return listView.canScrollList(direction);
                             ~~~~~~~~~~~~~
 2 errors, 0 warnings
         """.trimIndent()
 
         val expectedFix = """
-Fix for src/androidx/core/widget/ListViewCompat.java line 39: Extract to static inner class:
+Fix for src/androidx/sample/core/widget/ListViewCompat.java line 39: Extract to static inner class:
 @@ -39 +39
 -             listView.scrollListBy(y);
 +             Api19Impl.scrollListBy(listView, y);
@@ -106,7 +96,7 @@
 @@ -93 +102
 + }}
 +
-Fix for src/androidx/core/widget/ListViewCompat.java line 69: Extract to static inner class:
+Fix for src/androidx/sample/core/widget/ListViewCompat.java line 69: Extract to static inner class:
 @@ -69 +69
 -             return listView.canScrollList(direction);
 +             return Api19Impl.canScrollList(listView, direction);
@@ -134,7 +124,7 @@
     @Test
     fun `Auto-fix unsafe void-type method reference in Java source`() {
         val input = arrayOf(
-            javaSample("androidx.AutofixUnsafeVoidMethodReferenceJava")
+            javaSample("androidx.AutofixUnsafeVoidMethodReferenceJava"),
         )
 
         /* ktlint-disable max-line-length */
@@ -167,7 +157,7 @@
     @Test
     fun `Auto-fix unsafe constructor reference in Java source`() {
         val input = arrayOf(
-            javaSample("androidx.AutofixUnsafeConstructorReferenceJava")
+            javaSample("androidx.AutofixUnsafeConstructorReferenceJava"),
         )
 
         /* ktlint-disable max-line-length */
@@ -200,7 +190,7 @@
     @Test
     fun `Auto-fix unsafe static method reference in Java source`() {
         val input = arrayOf(
-            javaSample("androidx.AutofixUnsafeStaticMethodReferenceJava")
+            javaSample("androidx.AutofixUnsafeStaticMethodReferenceJava"),
         )
 
         /* ktlint-disable max-line-length */
@@ -233,7 +223,7 @@
     @Test
     fun `Auto-fix unsafe generic-type method reference in Java source`() {
         val input = arrayOf(
-            javaSample("androidx.AutofixUnsafeGenericMethodReferenceJava")
+            javaSample("androidx.AutofixUnsafeGenericMethodReferenceJava"),
         )
 
         /* ktlint-disable max-line-length */
@@ -266,7 +256,7 @@
     @Test
     fun `Auto-fix unsafe reference in Java source with existing inner class`() {
         val input = arrayOf(
-            javaSample("androidx.AutofixUnsafeReferenceWithExistingClassJava")
+            javaSample("androidx.AutofixUnsafeReferenceWithExistingClassJava"),
         )
 
         /* ktlint-disable max-line-length */
@@ -295,18 +285,4 @@
 
         check(*input).expectFixDiffs(expectedFix)
     }
-
-    /**
-     * Loads a [TestFile] from Java source code included in the JAR resources.
-     */
-    private fun javaSample(className: String): TestFile = TestFiles.java(
-        javaClass.getResource("/java/${className.replace('.', '/')}.java").readText()
-    )
-
-    /**
-     * Loads a [TestFile] from Kotlin source code included in the JAR resources.
-     */
-    private fun ktSample(className: String): TestFile = TestFiles.kotlin(
-        javaClass.getResource("/java/${className.replace('.', '/')}.kt").readText()
-    )
 }
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
index d3879db..0ed74a4 100644
--- a/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
@@ -14,30 +14,19 @@
  * limitations under the License.
  */
 
+@file:Suppress("UnstableApiUsage")
+
 package androidx.build.lint
 
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-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
 
-@Suppress("UnstableApiUsage")
 @RunWith(JUnit4::class)
-class IdeaSuppressionDetectorTest {
-
-    private fun check(
-        vararg testFiles: TestFile,
-    ): TestLintResult {
-        return lint()
-            .files(
-                *testFiles,
-            )
-            .issues(IdeaSuppressionDetector.ISSUE)
-            .run()
-    }
+class IdeaSuppressionDetectorTest : AbstractLintDetectorTest(
+    useDetector = IdeaSuppressionDetector(),
+    useIssues = listOf(IdeaSuppressionDetector.ISSUE),
+) {
 
     @Test
     fun `Detection of IDEA-specific suppression in Java sources`() {
@@ -56,11 +45,4 @@
 
         check(*input).expect(expected)
     }
-
-    /**
-     * Loads a [TestFile] from Java source code included in the JAR resources.
-     */
-    private fun javaSample(className: String): TestFile = TestFiles.java(
-        javaClass.getResource("/java/${className.replace('.', '/')}.java").readText()
-    )
-}
\ No newline at end of file
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt
new file mode 100644
index 0000000..f587614
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class MetadataTagInsideApplicationTagDetectorTest : AbstractLintDetectorTest(
+    useDetector = MetadataTagInsideApplicationTagDetector(),
+    useIssues = listOf(MetadataTagInsideApplicationTagDetector.ISSUE),
+) {
+
+    @Test
+    fun `Detect usage of metadata tag insice application tag`() {
+        val input = arrayOf(
+            manifestSample()
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+AndroidManifest.xml:20: Error: Detected <application>-level meta-data tag. [MetadataTagInsideApplicationTag]
+        <meta-data android:name="name" android:value="value" />
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
index deaab57..874cc32 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
@@ -14,40 +14,27 @@
  * limitations under the License.
  */
 
-package androidx.build.lint
+@file:Suppress("UnstableApiUsage")
 
-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
+package androidx.build.lint
 
 import org.junit.Ignore
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
 
 @Ignore("ANDROID_HOME not available on CI")
-class ObsoleteBuildCompatUsageDetectorTest {
-    private val buildCompatStub = java(
-        """
-        package androidx.core.os;
-        public class BuildCompat {
-          public static boolean isAtLeastN() { return false; }
-          public static boolean isAtLeastNMR1() { return false; }
-          public static boolean isAtLeastO() { return false; }
-          public static boolean isAtLeastOMR1() { return false; }
-          public static boolean isAtLeastP() { return false; }
-          public static boolean isAtLeastQ() { return false; }
-        }
-        """.trimIndent()
-    )
+@RunWith(JUnit4::class)
+class ObsoleteBuildCompatUsageDetectorTest : AbstractLintDetectorTest(
+    useDetector = ObsoleteBuildCompatUsageDetector(),
+    useIssues = listOf(ObsoleteBuildCompatUsageDetector.ISSUE),
+    stubs = arrayOf(BuildCompat),
+) {
 
-    private fun check(vararg code: String): TestLintResult {
-        return lint().files(buildCompatStub, *code.map(::java).toTypedArray())
-            .allowMissingSdk(true)
-            .issues(ObsoleteBuildCompatUsageDetector.ISSUE)
-            .run()
-    }
-
-    @Test fun isAtLeastN() {
-        val input = """
+    @Test
+    fun isAtLeastN() {
+        val input = java(
+            """
             package foo;
             import androidx.core.os.BuildCompat;
             public class Example {
@@ -57,26 +44,34 @@
                 }
               }
             }
-        """
+            """.trimIndent()
+        )
+
+        /* ktlint-disable max-line-length */
         val expected = """
             src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
                 if (BuildCompat.isAtLeastN()) {
                     ~~~~~~~~~~~~~~~~~~~~~~~~
             1 errors, 0 warnings
         """
+
         val expectedDiff = """
             Fix for src/foo/Example.java line 5: Use SDK_INT >= 24:
             @@ -5 +5
             -     if (BuildCompat.isAtLeastN()) {
             +     if (Build.VERSION.SDK_INT >= 24) {
         """
-        check(input.trimIndent())
+        /* ktlint-enable max-line-length */
+
+        check(input)
             .expect(expected.trimIndent())
             .expectFixDiffs(expectedDiff.trimIndent())
     }
 
-    @Test fun isAtLeastNStaticImport() {
-        val input = """
+    @Test
+    fun isAtLeastNStaticImport() {
+        val input = java(
+            """
             package foo;
             import static androidx.core.os.BuildCompat.isAtLeastN;
             public class Example {
@@ -86,26 +81,34 @@
                 }
               }
             }
-        """
+            """.trimIndent()
+        )
+
+        /* ktlint-disable max-line-length */
         val expected = """
             src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
                 if (isAtLeastN()) {
                     ~~~~~~~~~~~~
             1 errors, 0 warnings
         """
+
         val expectedDiff = """
             Fix for src/foo/Example.java line 5: Use SDK_INT >= 24:
             @@ -5 +5
             -     if (isAtLeastN()) {
             +     if (Build.VERSION.SDK_INT >= 24) {
         """
-        check(input.trimIndent())
+        /* ktlint-enable max-line-length */
+
+        check(input)
             .expect(expected.trimIndent())
             .expectFixDiffs(expectedDiff.trimIndent())
     }
 
-    @Test fun isAtLeastNMR1() {
-        val input = """
+    @Test
+    fun isAtLeastNMR1() {
+        val input = java(
+            """
             package foo;
             import androidx.core.os.BuildCompat;
             public class Example {
@@ -115,26 +118,34 @@
                 }
               }
             }
-        """
+            """.trimIndent()
+        )
+
+        /* ktlint-disable max-line-length */
         val expected = """
             src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
                 if (BuildCompat.isAtLeastNMR1()) {
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
             1 errors, 0 warnings
         """
+
         val expectedDiff = """
             Fix for src/foo/Example.java line 5: Use SDK_INT >= 25:
             @@ -5 +5
             -     if (BuildCompat.isAtLeastNMR1()) {
             +     if (Build.VERSION.SDK_INT >= 25) {
         """
-        check(input.trimIndent())
+        /* ktlint-enable max-line-length */
+
+        check(input)
             .expect(expected.trimIndent())
             .expectFixDiffs(expectedDiff.trimIndent())
     }
 
-    @Test fun isAtLeastO() {
-        val input = """
+    @Test
+    fun isAtLeastO() {
+        val input = java(
+            """
             package foo;
             import androidx.core.os.BuildCompat;
             public class Example {
@@ -144,26 +155,34 @@
                 }
               }
             }
-        """
+            """.trimIndent()
+        )
+
+        /* ktlint-disable max-line-length */
         val expected = """
             src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
                 if (BuildCompat.isAtLeastO()) {
                     ~~~~~~~~~~~~~~~~~~~~~~~~
             1 errors, 0 warnings
         """
+
         val expectedDiff = """
             Fix for src/foo/Example.java line 5: Use SDK_INT >= 26:
             @@ -5 +5
             -     if (BuildCompat.isAtLeastO()) {
             +     if (Build.VERSION.SDK_INT >= 26) {
         """
-        check(input.trimIndent())
+        /* ktlint-enable max-line-length */
+
+        check(input)
             .expect(expected.trimIndent())
             .expectFixDiffs(expectedDiff.trimIndent())
     }
 
-    @Test fun isAtLeastOMR1() {
-        val input = """
+    @Test
+    fun isAtLeastOMR1() {
+        val input = java(
+            """
             package foo;
             import androidx.core.os.BuildCompat;
             public class Example {
@@ -173,26 +192,34 @@
                 }
               }
             }
-        """
+            """.trimIndent()
+        )
+
+        /* ktlint-disable max-line-length */
         val expected = """
             src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
                 if (BuildCompat.isAtLeastOMR1()) {
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
             1 errors, 0 warnings
         """
+
         val expectedDiff = """
             Fix for src/foo/Example.java line 5: Use SDK_INT >= 27:
             @@ -5 +5
             -     if (BuildCompat.isAtLeastOMR1()) {
             +     if (Build.VERSION.SDK_INT >= 27) {
         """
-        check(input.trimIndent())
+        /* ktlint-enable max-line-length */
+
+        check(input)
             .expect(expected.trimIndent())
             .expectFixDiffs(expectedDiff.trimIndent())
     }
 
-    @Test fun isAtLeastP() {
-        val input = """
+    @Test
+    fun isAtLeastP() {
+        val input = java(
+            """
             package foo;
             import androidx.core.os.BuildCompat;
             public class Example {
@@ -202,26 +229,34 @@
                 }
               }
             }
-        """
+            """.trimIndent()
+        )
+
+        /* ktlint-disable max-line-length */
         val expected = """
             src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
                 if (BuildCompat.isAtLeastP()) {
                     ~~~~~~~~~~~~~~~~~~~~~~~~
             1 errors, 0 warnings
         """
+
         val expectedDiff = """
             Fix for src/foo/Example.java line 5: Use SDK_INT >= 28:
             @@ -5 +5
             -     if (BuildCompat.isAtLeastP()) {
             +     if (Build.VERSION.SDK_INT >= 28) {
         """
-        check(input.trimIndent())
+        /* ktlint-enable max-line-length */
+
+        check(input)
             .expect(expected.trimIndent())
             .expectFixDiffs(expectedDiff.trimIndent())
     }
 
-    @Test fun isAtLeastQ() {
-        val input = """
+    @Test
+    fun isAtLeastQ() {
+        val input = java(
+            """
             package foo;
             import androidx.core.os.BuildCompat;
             public class Example {
@@ -231,21 +266,43 @@
                 }
               }
             }
-        """
+            """.trimIndent()
+        )
+
+        /* ktlint-disable max-line-length */
         val expected = """
             src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
                 if (BuildCompat.isAtLeastQ()) {
                     ~~~~~~~~~~~~~~~~~~~~~~~~
             1 errors, 0 warnings
         """
+
         val expectedDiff = """
             Fix for src/foo/Example.java line 5: Use SDK_INT >= 29:
             @@ -5 +5
             -     if (BuildCompat.isAtLeastQ()) {
             +     if (Build.VERSION.SDK_INT >= 29) {
         """
-        check(input.trimIndent())
+        /* ktlint-enable max-line-length */
+
+        check(input)
             .expect(expected.trimIndent())
             .expectFixDiffs(expectedDiff.trimIndent())
     }
+
+    companion object {
+        private val BuildCompat = java(
+            """
+            package androidx.core.os;
+            public class BuildCompat {
+              public static boolean isAtLeastN() { return false; }
+              public static boolean isAtLeastNMR1() { return false; }
+              public static boolean isAtLeastO() { return false; }
+              public static boolean isAtLeastOMR1() { return false; }
+              public static boolean isAtLeastP() { return false; }
+              public static boolean isAtLeastQ() { return false; }
+            }
+            """.trimIndent()
+        )
+    }
 }
diff --git a/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
index 0d51308..b8ac0d9 100644
--- a/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
@@ -18,24 +18,15 @@
 
 package androidx.build.lint
 
-import com.android.tools.lint.checks.infrastructure.TestFile
-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 PrivateConstructorForUtilityClassDetectorTest {
-
-    private fun check(
-        vararg testFiles: TestFile,
-    ): TestLintResult {
-        return lint()
-            .files(*testFiles)
-            .issues(PrivateConstructorForUtilityClassDetector.ISSUE)
-            .run()
-    }
+class PrivateConstructorForUtilityClassDetectorTest : AbstractLintDetectorTest(
+    useDetector = PrivateConstructorForUtilityClassDetector(),
+    useIssues = listOf(PrivateConstructorForUtilityClassDetector.ISSUE),
+) {
 
     @Test
     fun testInnerClassVisibilityJava() {
diff --git a/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt b/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
index 2c638cb..0e6943f 100644
--- a/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-@file:Suppress("KDocUnresolvedReference")
+@file:Suppress("KDocUnresolvedReference", "UnstableApiUsage")
 
 package androidx.build.lint
 
diff --git a/lint-checks/src/test/java/androidx/build/lint/Stubs.kt b/lint-checks/src/test/java/androidx/build/lint/Stubs.kt
new file mode 100644
index 0000000..06a6353
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/Stubs.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2021 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+
+class Stubs {
+
+    companion object {
+
+        /* ktlint-disable max-line-length */
+
+        /**
+         * [TestFile] containing Keep.java from the annotation library.
+         */
+        val Keep = TestFiles.java(
+            """
+package androidx.annotation;
+
+public @interface Keep {
+}
+            """
+        )
+
+        val RunWith = TestFiles.kotlin(
+            """
+package org.junit.runner
+
+annotation class RunWith(val value: KClass<*>)
+            """
+        )
+
+        val JUnit4Runner = TestFiles.kotlin(
+            """
+package org.junit.runners
+
+class JUnit4
+            """
+        )
+
+        val ParameterizedRunner = TestFiles.kotlin(
+            """
+package org.junit.runners
+
+class Parameterized
+            """
+        )
+
+        val AndroidJUnit4Runner = TestFiles.kotlin(
+            """
+package androidx.test.ext.junit.runners
+
+class AndroidJUnit4
+            """
+        )
+
+        val TestSizeAnnotations = TestFiles.kotlin(
+            """
+package androidx.test.filters
+
+annotation class SmallTest
+annotation class MediumTest
+annotation class LargeTest
+            """
+        )
+
+        val TestAnnotation = TestFiles.kotlin(
+            """
+package org.junit
+
+annotation class Test
+            """
+        )
+
+        /**
+         * [TestFile] containing OptIn.kt from the Kotlin standard library.
+         *
+         * This is a workaround for the Kotlin standard library used by the Lint test harness not
+         * including the Experimental annotation by default.
+         */
+        val OptIn = TestFiles.kotlin(
+            """
+package kotlin
+
+import kotlin.annotation.AnnotationRetention.BINARY
+import kotlin.annotation.AnnotationRetention.SOURCE
+import kotlin.annotation.AnnotationTarget.*
+import kotlin.internal.RequireKotlin
+import kotlin.internal.RequireKotlinVersionKind
+import kotlin.reflect.KClass
+
+@Target(ANNOTATION_CLASS)
+@Retention(BINARY)
+@SinceKotlin("1.3")
+@RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
+public annotation class RequiresOptIn(
+    val message: String = "",
+    val level: Level = Level.ERROR
+) {
+    public enum class Level {
+        WARNING,
+        ERROR,
+    }
+}
+
+@Target(
+    CLASS, PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, EXPRESSION, FILE, TYPEALIAS
+)
+@Retention(SOURCE)
+@SinceKotlin("1.3")
+@RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
+public annotation class OptIn(
+    vararg val markerClass: KClass<out Annotation>
+)
+            """
+        )
+
+        /**
+         * [TestFile] containing ChecksSdkIntAtLeast.java from the annotation library.
+         */
+        val ChecksSdkIntAtLeast = TestFiles.java(
+            """
+package androidx.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Documented
+@Retention(CLASS)
+@Target({METHOD, FIELD})
+public @interface ChecksSdkIntAtLeast {
+    int api() default -1;
+    String codename() default "";
+    int parameter() default -1;
+    int lambda() default -1;
+}
+            """
+        )
+
+        /* ktlint-enable max-line-length */
+    }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt b/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
index 08933de3..fe6ce7d 100644
--- a/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
@@ -19,7 +19,6 @@
 package androidx.build.lint
 
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Test
@@ -53,7 +52,7 @@
                 }
             """
             ).within("src/test"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expectClean()
@@ -85,7 +84,7 @@
                 }
             """
             ).within("src/test"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expect(
@@ -114,7 +113,7 @@
                 class Test
             """
             ).within("src/androidTest"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expect(
@@ -148,7 +147,7 @@
                 }
             """
             ).within("src/androidTest"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expectClean()
@@ -171,7 +170,7 @@
                 }
             """
             ).within("src/androidTest"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expectClean()
@@ -218,7 +217,7 @@
                 }
             """
             ).within("src/androidTest"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expectClean()
@@ -249,7 +248,7 @@
                 }
             """
             ).within("src/androidTest"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expect(
@@ -287,7 +286,7 @@
                 }
             """
             ).within("src/androidTest"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expect(
@@ -332,68 +331,18 @@
                 }
             """
             ).within("src/androidTest"),
-            *Stubs
+            *StubClasses
         )
             .run()
             .expectClean()
     }
+
+    private val StubClasses = arrayOf(
+        Stubs.RunWith,
+        Stubs.JUnit4Runner,
+        Stubs.ParameterizedRunner,
+        Stubs.AndroidJUnit4Runner,
+        Stubs.TestSizeAnnotations,
+        Stubs.TestAnnotation
+    )
 }
-
-private val RunWith = kotlin(
-    """
-        package org.junit.runner
-
-        annotation class RunWith(val value: KClass<*>)
-    """
-)
-
-private val JUnit4Runner = kotlin(
-    """
-        package org.junit.runners
-
-        class JUnit4
-    """
-)
-
-private val ParameterizedRunner = kotlin(
-    """
-        package org.junit.runners
-
-        class Parameterized
-    """
-)
-
-private val AndroidJUnit4Runner = kotlin(
-    """
-        package androidx.test.ext.junit.runners
-
-        class AndroidJUnit4
-    """
-)
-
-private val TestSizeAnnotations = kotlin(
-    """
-        package androidx.test.filters
-
-        annotation class SmallTest
-        annotation class MediumTest
-        annotation class LargeTest
-    """
-)
-
-private val TestAnnotation = kotlin(
-    """
-        package org.junit
-
-        annotation class Test
-    """
-)
-
-private val Stubs = arrayOf(
-    RunWith,
-    JUnit4Runner,
-    ParameterizedRunner,
-    AndroidJUnit4Runner,
-    TestSizeAnnotations,
-    TestAnnotation
-)
diff --git a/lint-checks/src/test/java/androidx/build/lint/TestUtils.kt b/lint-checks/src/test/java/androidx/build/lint/TestUtils.kt
deleted file mode 100644
index b863b8b..0000000
--- a/lint-checks/src/test/java/androidx/build/lint/TestUtils.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-@file:Suppress("UnstableApiUsage")
-
-package androidx.build.lint
-
-import com.android.tools.lint.checks.infrastructure.ProjectDescription
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-import java.io.FileNotFoundException
-
-private class TestUtils
-
-fun project(): ProjectDescription = ProjectDescription()
-
-/**
- * Loads a [TestFile] from Java source code included in the JAR resources.
- */
-fun javaSample(className: String): TestFile = TestFiles.java(
-    TestUtils::class.java.getResource(
-        "/java/${className.replace('.', '/')}.java"
-    )?.readText() ?: throw FileNotFoundException(
-        "Could not find Java sources for $className in the integration test project"
-    )
-)
-
-/**
- * Loads a [TestFile] from Kotlin source code included in the JAR resources.
- */
-fun ktSample(className: String): TestFile = TestFiles.kotlin(
-    TestUtils::class.java.getResource(
-        "/java/${className.replace('.', '/')}.kt"
-    )?.readText() ?: throw FileNotFoundException(
-        "Could not find Kotlin sources for $className in the integration test project"
-    )
-)
\ No newline at end of file
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 561b834..214cdab 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -26,6 +26,7 @@
 
 dependencies {
     api("androidx.core:core:1.3.0")
+    implementation("androidx.annotation:annotation:1.2.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/media/media/lint-baseline.xml b/media/media/lint-baseline.xml
index 1fe0359..2386b46 100644
--- a/media/media/lint-baseline.xml
+++ b/media/media/lint-baseline.xml
@@ -2,1135 +2,13 @@
 <issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mFwkBuilder = new AudioAttributes.Builder();"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
-            line="133"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mFwkBuilder = new AudioAttributes.Builder((AudioAttributes) aa);"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
-            line="137"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return new AudioAttributesImplApi21(mFwkBuilder.build());"
-        errorLine2="                                                            ~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
-            line="143"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mFwkBuilder.setUsage(usage);"
-        errorLine2="                        ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
-            line="153"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mFwkBuilder.setContentType(contentType);"
-        errorLine2="                        ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
-            line="160"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mFwkBuilder.setFlags(flags);"
-        errorLine2="                        ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
-            line="167"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mFwkBuilder.setLegacyStreamType(streamType);"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
-            line="174"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi26.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return new AudioAttributesImplApi26(mFwkBuilder.build());"
-        errorLine2="                                                            ~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi26.java"
-            line="65"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi26.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mFwkBuilder.setUsage(usage);"
-        errorLine2="                        ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioAttributesImplApi26.java"
-            line="71"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    new AudioFocusRequest.Builder(mFocusGain)"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
-            line="84"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                            .setAudioAttributes(getAudioAttributes())"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
-            line="85"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                            .setWillPauseWhenDucked(mPauseOnDuck)"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
-            line="86"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                            .setOnAudioFocusChangeListener("
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
-            line="87"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                            .build();"
-        errorLine2="                             ~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
-            line="89"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.AudioManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return audioManager.requestAudioFocus(focusRequest.getAudioFocusRequest());"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioManagerCompat.java"
-            line="91"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.AudioManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return audioManager.abandonAudioFocusRequest(focusRequest.getAudioFocusRequest());"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioManagerCompat.java"
-            line="120"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.media.AudioManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return audioManager.getStreamMinVolume(streamType);"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/AudioManagerCompat.java"
-            line="148"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaBrowserCompat.MediaItem is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            int flags = itemFwk.getFlags();"
-        errorLine2="                                ~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="499"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaBrowserCompat.MediaItem is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    MediaDescriptionCompat.fromMediaDescription(itemFwk.getDescription());"
-        errorLine2="                                                                        ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="501"
-            column="73"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    mServiceFwk.setSessionToken((MediaSession.Token) token.getToken());"
-        errorLine2="                                ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="335"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.MediaBrowserServiceCompat.MediaBrowserServiceImplApi21.MediaBrowserServiceApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                return browserRootCompat == null ? null : new MediaBrowserService.BrowserRoot("
-        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="506"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.media.session.MediaButtonReceiver is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.startForegroundService(intent);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="301"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="169"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            activity.setMediaController(controllerFwk);"
-        errorLine2="                     ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="171"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            MediaController controllerFwk = activity.getMediaController();"
-        errorLine2="                                                     ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="192"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();"
-        errorLine2="                                                               ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="196"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class android.support.v4.media.session.MediaControllerCompat.MediaControllerImplApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mSessionInfo = mControllerFwk.getSessionInfo();"
-        errorLine2="                                              ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="2270"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mControlsFwk.prepare();"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="2394"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mControlsFwk.prepareFromMediaId(mediaId, extras);"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="2403"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mControlsFwk.prepareFromSearch(query, extras);"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="2415"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mControlsFwk.prepareFromUri(uri, extras);"
-        errorLine2="                             ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="2427"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mControlsFwk.setPlaybackSpeed(speed);"
-        errorLine2="                             ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="2495"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mControlsFwk.playFromUri(uri, extras);"
-        errorLine2="                             ~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="2537"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        MediaDescription.Builder bob = new MediaDescription.Builder();"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="339"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        bob.setMediaId(mMediaId);"
-        errorLine2="            ~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="340"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        bob.setTitle(mTitle);"
-        errorLine2="            ~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="341"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        bob.setSubtitle(mSubtitle);"
-        errorLine2="            ~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="342"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        bob.setDescription(mDescription);"
-        errorLine2="            ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="343"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        bob.setIconBitmap(mIcon);"
-        errorLine2="            ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="344"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        bob.setIconUri(mIconUri);"
-        errorLine2="            ~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="345"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        bob.setExtras(extras);"
-        errorLine2="            ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="358"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            bob.setMediaUri(mMediaUri);"
-        errorLine2="                ~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="360"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mDescriptionFwk = bob.build();"
-        errorLine2="                              ~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="362"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            bob.setMediaId(description.getMediaId());"
-        errorLine2="                                       ~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="383"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            bob.setTitle(description.getTitle());"
-        errorLine2="                                     ~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="384"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            bob.setSubtitle(description.getSubtitle());"
-        errorLine2="                                        ~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="385"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            bob.setDescription(description.getDescription());"
-        errorLine2="                                           ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="386"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            bob.setIconBitmap(description.getIconBitmap());"
-        errorLine2="                                          ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="387"
-            column="43"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            bob.setIconUri(description.getIconUri());"
-        errorLine2="                                       ~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="388"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            Bundle extras = description.getExtras();"
-        errorLine2="                                        ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="389"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                bob.setMediaUri(description.getMediaUri());"
-        errorLine2="                                            ~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="415"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class android.support.v4.media.session.MediaSessionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return new MediaSession(context, tag, sessionInfo);"
-        errorLine2="                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="613"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return new MediaSession(context, tag);"
-        errorLine2="                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="615"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat.QueueItem is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mItemFwk = new MediaSession.QueueItem("
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2231"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat.QueueItem is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            Object descriptionObj = queueItemObj.getDescription();"
-        errorLine2="                                                 ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2252"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat.QueueItem is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            long id = queueItemObj.getQueueId();"
-        errorLine2="                                   ~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2255"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class android.support.v4.media.session.MediaSessionCompat.MediaSessionImplApi21 is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mSessionFwk.setRatingType(type);"
-        errorLine2="                            ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="3974"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 16; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                builder.getBuilder().setStyle("
-        errorLine2="                                     ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="211"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        fillInMediaStyle(new Notification.MediaStyle()));"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="212"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                style.setShowActionsInCompactView(mActionsToShowInCompact);"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="221"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                style.setMediaSession((MediaSession.Token) mToken.getToken());"
-        errorLine2="                      ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="224"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 15; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                button.setContentDescription(R.id.action0, action.getTitle());"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="288"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 16; however, the containing class androidx.media.app.NotificationCompat.DecoratedMediaCustomViewStyle is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                builder.getBuilder().setStyle("
-        errorLine2="                                     ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="389"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.media.app.NotificationCompat.DecoratedMediaCustomViewStyle is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        fillInMediaStyle(new Notification.DecoratedMediaCustomViewStyle()));"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="390"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            List&lt;PlaybackState.CustomAction> customActionFwks = stateFwk.getCustomActions();"
-        errorLine2="                                                                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="809"
-            column="74"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                extras = stateFwk.getExtras();"
-        errorLine2="                                  ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="819"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getState(),"
-        errorLine2="                             ~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="825"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getPosition(),"
-        errorLine2="                             ~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="826"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getBufferedPosition(),"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="827"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getPlaybackSpeed(),"
-        errorLine2="                             ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="828"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getActions(),"
-        errorLine2="                             ~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="829"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getErrorMessage(),"
-        errorLine2="                             ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="831"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getLastPositionUpdateTime(),"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="832"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    stateFwk.getActiveQueueItemId(),"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="834"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            PlaybackState.Builder builder = new PlaybackState.Builder();"
-        errorLine2="                                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="853"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setState(mState, mPosition, mSpeed, mUpdateTime);"
-        errorLine2="                    ~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="854"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setBufferedPosition(mBufferedPosition);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="855"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setActions(mActions);"
-        errorLine2="                    ~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="856"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setErrorMessage(mErrorMessage);"
-        errorLine2="                    ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="857"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                builder.addCustomAction("
-        errorLine2="                        ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="859"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setActiveQueueItemId(mActiveItemId);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="862"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                builder.setExtras(mExtras);"
-        errorLine2="                        ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="864"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mStateFwk = builder.build();"
-        errorLine2="                                ~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="866"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            Bundle extras = customActionFwk.getExtras();"
-        errorLine2="                                            ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="945"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                            customActionFwk.getAction(),"
-        errorLine2="                                            ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="949"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                            customActionFwk.getName(),"
-        errorLine2="                                            ~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="950"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                            customActionFwk.getIcon(),"
-        errorLine2="                                            ~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="951"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            PlaybackState.CustomAction.Builder builder = new PlaybackState.CustomAction.Builder("
-        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="972"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setExtras(mExtras);"
-        errorLine2="                    ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="974"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return builder.build();"
-        errorLine2="                           ~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="975"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            final int ratingStyle = ((Rating) ratingObj).getRatingStyle();"
-        errorLine2="                                                         ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="334"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (((Rating) ratingObj).isRated()) {"
-        errorLine2="                                     ~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="336"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        rating = newHeartRating(((Rating) ratingObj).hasHeart());"
-        errorLine2="                                                                     ~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="339"
-            column="70"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        rating = newThumbRating(((Rating) ratingObj).isThumbUp());"
-        errorLine2="                                                                     ~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="342"
-            column="70"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                                ((Rating) ratingObj).getStarRating());"
-        errorLine2="                                                     ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="348"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                                ((Rating) ratingObj).getPercentRating());"
-        errorLine2="                                                     ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="352"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        mRatingObj = Rating.newHeartRating(hasHeart());"
-        errorLine2="                                            ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="380"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        mRatingObj = Rating.newThumbRating(isThumbUp());"
-        errorLine2="                                            ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="383"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        mRatingObj = Rating.newStarRating(mRatingStyle,"
-        errorLine2="                                            ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="388"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        mRatingObj = Rating.newPercentageRating(getPercentRating());"
-        errorLine2="                                            ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="392"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mRatingObj = Rating.newUnratedRating(mRatingStyle);"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="398"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.media.VolumeProviderCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            volumeProviderFwk.setCurrentVolume(currentVolume);"
-        errorLine2="                              ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/VolumeProviderCompat.java"
-            line="146"
-            column="31"/>
-    </issue>
-
-    <issue
         id="LambdaLast"
         message="Functional interface parameters (such as parameter 1, &quot;listener&quot;, in androidx.media.AudioFocusRequestCompat.Builder.setOnAudioFocusChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
         errorLine1="                @NonNull OnAudioFocusChangeListener listener, @NonNull Handler handler) {"
         errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
-            line="310"
+            line="306"
             column="63"/>
     </issue>
 
@@ -1229,7 +107,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
-            line="370"
+            line="366"
             column="16"/>
     </issue>
 
@@ -1240,7 +118,7 @@
         errorLine2="                              ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="190"
+            line="192"
             column="31"/>
     </issue>
 
@@ -1251,7 +129,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="190"
+            line="192"
             column="48"/>
     </issue>
 
@@ -1262,7 +140,7 @@
         errorLine2="            ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="191"
+            line="193"
             column="13"/>
     </issue>
 
@@ -1273,7 +151,7 @@
         errorLine2="                                         ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="191"
+            line="193"
             column="42"/>
     </issue>
 
@@ -1284,7 +162,7 @@
         errorLine2="                                                          ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="410"
+            line="412"
             column="59"/>
     </issue>
 
@@ -1295,7 +173,7 @@
         errorLine2="                                                         ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="432"
+            line="434"
             column="58"/>
     </issue>
 
@@ -1306,7 +184,7 @@
         errorLine2="                      ~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="494"
+            line="496"
             column="23"/>
     </issue>
 
@@ -1317,7 +195,7 @@
         errorLine2="                                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="494"
+            line="496"
             column="47"/>
     </issue>
 
@@ -1328,7 +206,7 @@
         errorLine2="                      ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="515"
+            line="517"
             column="23"/>
     </issue>
 
@@ -1339,7 +217,7 @@
         errorLine2="                                                        ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="515"
+            line="517"
             column="57"/>
     </issue>
 
@@ -1350,7 +228,7 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="557"
+            line="559"
             column="35"/>
     </issue>
 
@@ -1361,7 +239,7 @@
         errorLine2="                                 ~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="883"
+            line="885"
             column="34"/>
     </issue>
 
@@ -1372,7 +250,7 @@
         errorLine2="                                                          ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="922"
+            line="924"
             column="59"/>
     </issue>
 
@@ -1383,7 +261,7 @@
         errorLine2="                                                   ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="933"
+            line="935"
             column="52"/>
     </issue>
 
@@ -1394,7 +272,7 @@
         errorLine2="                                     ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="949"
+            line="951"
             column="38"/>
     </issue>
 
@@ -1405,7 +283,7 @@
         errorLine2="                                                    ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="949"
+            line="951"
             column="53"/>
     </issue>
 
@@ -1416,7 +294,7 @@
         errorLine2="                                                                   ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="949"
+            line="951"
             column="68"/>
     </issue>
 
@@ -1427,7 +305,7 @@
         errorLine2="                             ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="959"
+            line="961"
             column="30"/>
     </issue>
 
@@ -1438,7 +316,7 @@
         errorLine2="                                            ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="959"
+            line="961"
             column="45"/>
     </issue>
 
@@ -1449,7 +327,7 @@
         errorLine2="                                                           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="959"
+            line="961"
             column="60"/>
     </issue>
 
@@ -1460,7 +338,7 @@
         errorLine2="                            ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="970"
+            line="972"
             column="29"/>
     </issue>
 
@@ -1471,7 +349,7 @@
         errorLine2="                                           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="970"
+            line="972"
             column="44"/>
     </issue>
 
@@ -1482,7 +360,7 @@
         errorLine2="                                                          ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
-            line="970"
+            line="972"
             column="59"/>
     </issue>
 
@@ -1537,7 +415,7 @@
         errorLine2="                                    ~~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1306"
+            line="1311"
             column="37"/>
     </issue>
 
@@ -1548,7 +426,7 @@
         errorLine2="           ~~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1328"
+            line="1333"
             column="12"/>
     </issue>
 
@@ -1559,7 +437,7 @@
         errorLine2="                          ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1328"
+            line="1333"
             column="27"/>
     </issue>
 
@@ -1570,7 +448,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1333"
+            line="1338"
             column="22"/>
     </issue>
 
@@ -1581,7 +459,7 @@
         errorLine2="                                        ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1333"
+            line="1338"
             column="41"/>
     </issue>
 
@@ -1592,7 +470,7 @@
         errorLine2="                                                            ~~~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1333"
+            line="1338"
             column="61"/>
     </issue>
 
@@ -1603,7 +481,7 @@
         errorLine2="                            ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1424"
+            line="1429"
             column="29"/>
     </issue>
 
@@ -1614,7 +492,7 @@
         errorLine2="                                       ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1424"
+            line="1429"
             column="40"/>
     </issue>
 
@@ -1625,7 +503,7 @@
         errorLine2="                              ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1434"
+            line="1439"
             column="31"/>
     </issue>
 
@@ -1636,7 +514,7 @@
         errorLine2="                           ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1455"
+            line="1460"
             column="28"/>
     </issue>
 
@@ -1647,7 +525,7 @@
         errorLine2="                                                ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1481"
+            line="1486"
             column="49"/>
     </issue>
 
@@ -1658,7 +536,7 @@
         errorLine2="                                                       ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1506"
+            line="1511"
             column="56"/>
     </issue>
 
@@ -1669,7 +547,7 @@
         errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1519"
+            line="1524"
             column="33"/>
     </issue>
 
@@ -1680,7 +558,7 @@
         errorLine2="                 ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1554"
+            line="1559"
             column="18"/>
     </issue>
 
@@ -1691,7 +569,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1962"
+            line="1967"
             column="16"/>
     </issue>
 
@@ -1702,7 +580,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
-            line="1969"
+            line="1974"
             column="16"/>
     </issue>
 
@@ -1713,7 +591,7 @@
         errorLine2="                          ~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="106"
+            line="107"
             column="27"/>
     </issue>
 
@@ -1724,7 +602,7 @@
         errorLine2="                                           ~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="106"
+            line="107"
             column="44"/>
     </issue>
 
@@ -1735,7 +613,7 @@
         errorLine2="                  ~~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="191"
+            line="192"
             column="19"/>
     </issue>
 
@@ -1746,7 +624,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="191"
+            line="192"
             column="41"/>
     </issue>
 
@@ -1757,7 +635,7 @@
         errorLine2="                                                                               ~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="191"
+            line="192"
             column="80"/>
     </issue>
 
@@ -1768,7 +646,7 @@
         errorLine2="                  ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="224"
+            line="225"
             column="19"/>
     </issue>
 
@@ -1779,7 +657,7 @@
         errorLine2="                                                              ~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="224"
+            line="225"
             column="63"/>
     </issue>
 
@@ -1790,7 +668,7 @@
         errorLine2="                  ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="258"
+            line="259"
             column="19"/>
     </issue>
 
@@ -1801,7 +679,7 @@
         errorLine2="                                                              ~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="258"
+            line="259"
             column="63"/>
     </issue>
 
@@ -1812,7 +690,7 @@
         errorLine2="            ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="259"
+            line="260"
             column="13"/>
     </issue>
 
@@ -1823,7 +701,7 @@
         errorLine2="                  ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="283"
+            line="284"
             column="19"/>
     </issue>
 
@@ -1834,7 +712,7 @@
         errorLine2="                                                                ~~~~~~~">
         <location
             file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
-            line="283"
+            line="284"
             column="65"/>
     </issue>
 
@@ -1856,7 +734,7 @@
         errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="186"
+            line="181"
             column="19"/>
     </issue>
 
@@ -1867,7 +745,7 @@
         errorLine2="                                 ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="235"
+            line="224"
             column="34"/>
     </issue>
 
@@ -1878,7 +756,7 @@
         errorLine2="                                 ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="254"
+            line="245"
             column="34"/>
     </issue>
 
@@ -1889,7 +767,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="272"
+            line="263"
             column="12"/>
     </issue>
 
@@ -1900,7 +778,7 @@
         errorLine2="                                            ~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="283"
+            line="274"
             column="45"/>
     </issue>
 
@@ -1911,7 +789,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="300"
+            line="291"
             column="12"/>
     </issue>
 
@@ -1922,7 +800,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="309"
+            line="300"
             column="12"/>
     </issue>
 
@@ -1933,7 +811,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="319"
+            line="310"
             column="12"/>
     </issue>
 
@@ -1944,7 +822,7 @@
         errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="335"
+            line="326"
             column="30"/>
     </issue>
 
@@ -1955,7 +833,7 @@
         errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="355"
+            line="346"
             column="30"/>
     </issue>
 
@@ -1966,7 +844,7 @@
         errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="372"
+            line="363"
             column="33"/>
     </issue>
 
@@ -1977,7 +855,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="402"
+            line="393"
             column="12"/>
     </issue>
 
@@ -1988,7 +866,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="409"
+            line="400"
             column="12"/>
     </issue>
 
@@ -1999,7 +877,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="489"
+            line="480"
             column="12"/>
     </issue>
 
@@ -2010,7 +888,7 @@
         errorLine2="           ~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="499"
+            line="490"
             column="12"/>
     </issue>
 
@@ -2021,7 +899,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="508"
+            line="499"
             column="12"/>
     </issue>
 
@@ -2032,7 +910,7 @@
         errorLine2="                                                             ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="576"
+            line="567"
             column="62"/>
     </issue>
 
@@ -2043,7 +921,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="654"
+            line="645"
             column="12"/>
     </issue>
 
@@ -2054,7 +932,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="684"
+            line="675"
             column="12"/>
     </issue>
 
@@ -2065,7 +943,7 @@
         errorLine2="                                   ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="729"
+            line="720"
             column="36"/>
     </issue>
 
@@ -2076,7 +954,7 @@
         errorLine2="                                                 ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="729"
+            line="720"
             column="50"/>
     </issue>
 
@@ -2087,7 +965,7 @@
         errorLine2="                                           ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="737"
+            line="728"
             column="44"/>
     </issue>
 
@@ -2098,7 +976,7 @@
         errorLine2="                                      ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="746"
+            line="737"
             column="39"/>
     </issue>
 
@@ -2109,7 +987,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="757"
+            line="748"
             column="36"/>
     </issue>
 
@@ -2120,7 +998,7 @@
         errorLine2="                                        ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="767"
+            line="758"
             column="41"/>
     </issue>
 
@@ -2131,7 +1009,7 @@
         errorLine2="                                    ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="776"
+            line="767"
             column="37"/>
     </issue>
 
@@ -2142,7 +1020,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="784"
+            line="775"
             column="40"/>
     </issue>
 
@@ -2153,7 +1031,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="823"
+            line="814"
             column="16"/>
     </issue>
 
@@ -2164,7 +1042,7 @@
         errorLine2="                                                ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1174"
+            line="1165"
             column="49"/>
     </issue>
 
@@ -2175,7 +1053,7 @@
         errorLine2="                                                                ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1174"
+            line="1165"
             column="65"/>
     </issue>
 
@@ -2186,7 +1064,7 @@
         errorLine2="                                               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1186"
+            line="1177"
             column="48"/>
     </issue>
 
@@ -2197,7 +1075,7 @@
         errorLine2="                                                             ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1186"
+            line="1177"
             column="62"/>
     </issue>
 
@@ -2208,7 +1086,7 @@
         errorLine2="                                            ~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1197"
+            line="1188"
             column="45"/>
     </issue>
 
@@ -2219,7 +1097,7 @@
         errorLine2="                                                     ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1197"
+            line="1188"
             column="54"/>
     </issue>
 
@@ -2230,7 +1108,7 @@
         errorLine2="                                             ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1211"
+            line="1202"
             column="46"/>
     </issue>
 
@@ -2241,7 +1119,7 @@
         errorLine2="                                                             ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1211"
+            line="1202"
             column="62"/>
     </issue>
 
@@ -2252,7 +1130,7 @@
         errorLine2="                                            ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1222"
+            line="1213"
             column="45"/>
     </issue>
 
@@ -2263,7 +1141,7 @@
         errorLine2="                                                          ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1222"
+            line="1213"
             column="59"/>
     </issue>
 
@@ -2274,7 +1152,7 @@
         errorLine2="                                         ~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1231"
+            line="1222"
             column="42"/>
     </issue>
 
@@ -2285,7 +1163,7 @@
         errorLine2="                                                  ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1231"
+            line="1222"
             column="51"/>
     </issue>
 
@@ -2296,7 +1174,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1287"
+            line="1278"
             column="40"/>
     </issue>
 
@@ -2307,7 +1185,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1301"
+            line="1292"
             column="40"/>
     </issue>
 
@@ -2318,7 +1196,7 @@
         errorLine2="                                                            ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1301"
+            line="1292"
             column="61"/>
     </issue>
 
@@ -2329,7 +1207,7 @@
         errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1347"
+            line="1338"
             column="47"/>
     </issue>
 
@@ -2340,7 +1218,7 @@
         errorLine2="                ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1348"
+            line="1339"
             column="17"/>
     </issue>
 
@@ -2351,7 +1229,7 @@
         errorLine2="                                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1366"
+            line="1357"
             column="47"/>
     </issue>
 
@@ -2362,7 +1240,7 @@
         errorLine2="                                                             ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
-            line="1366"
+            line="1357"
             column="62"/>
     </issue>
 
@@ -2373,7 +1251,7 @@
         errorLine2="                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="304"
+            line="306"
             column="31"/>
     </issue>
 
@@ -2384,7 +1262,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="335"
+            line="337"
             column="12"/>
     </issue>
 
@@ -2395,7 +1273,7 @@
         errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="379"
+            line="381"
             column="19"/>
     </issue>
 
@@ -2406,7 +1284,7 @@
         errorLine2="                                                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="379"
+            line="381"
             column="63"/>
     </issue>
 
@@ -2417,7 +1295,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="468"
+            line="470"
             column="16"/>
     </issue>
 
@@ -2428,7 +1306,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="479"
+            line="481"
             column="16"/>
     </issue>
 
@@ -2439,7 +1317,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="490"
+            line="492"
             column="16"/>
     </issue>
 
@@ -2450,7 +1328,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="502"
+            line="504"
             column="16"/>
     </issue>
 
@@ -2461,7 +1339,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="514"
+            line="516"
             column="16"/>
     </issue>
 
@@ -2472,7 +1350,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="526"
+            line="528"
             column="16"/>
     </issue>
 
@@ -2483,7 +1361,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="537"
+            line="539"
             column="16"/>
     </issue>
 
@@ -2494,7 +1372,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="548"
+            line="550"
             column="16"/>
     </issue>
 
@@ -2505,7 +1383,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
-            line="559"
+            line="561"
             column="16"/>
     </issue>
 
@@ -2890,7 +1768,7 @@
         errorLine2="                            ~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="629"
+            line="619"
             column="29"/>
     </issue>
 
@@ -2901,7 +1779,7 @@
         errorLine2="                            ~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="645"
+            line="635"
             column="29"/>
     </issue>
 
@@ -2912,7 +1790,7 @@
         errorLine2="                                               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="645"
+            line="635"
             column="48"/>
     </issue>
 
@@ -2923,7 +1801,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="661"
+            line="651"
             column="36"/>
     </issue>
 
@@ -2934,7 +1812,7 @@
         errorLine2="                                       ~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="678"
+            line="668"
             column="40"/>
     </issue>
 
@@ -2945,7 +1823,7 @@
         errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="720"
+            line="710"
             column="37"/>
     </issue>
 
@@ -2956,7 +1834,7 @@
         errorLine2="                                 ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="764"
+            line="754"
             column="34"/>
     </issue>
 
@@ -2967,7 +1845,7 @@
         errorLine2="                                               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="764"
+            line="754"
             column="48"/>
     </issue>
 
@@ -2978,7 +1856,7 @@
         errorLine2="           ~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="794"
+            line="784"
             column="12"/>
     </issue>
 
@@ -2989,7 +1867,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="804"
+            line="794"
             column="12"/>
     </issue>
 
@@ -3000,7 +1878,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="813"
+            line="803"
             column="34"/>
     </issue>
 
@@ -3011,7 +1889,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="825"
+            line="815"
             column="29"/>
     </issue>
 
@@ -3022,7 +1900,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="841"
+            line="831"
             column="26"/>
     </issue>
 
@@ -3033,7 +1911,7 @@
         errorLine2="                              ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="865"
+            line="855"
             column="31"/>
     </issue>
 
@@ -3044,7 +1922,7 @@
         errorLine2="                          ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="934"
+            line="924"
             column="27"/>
     </issue>
 
@@ -3055,7 +1933,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="948"
+            line="938"
             column="12"/>
     </issue>
 
@@ -3066,7 +1944,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="962"
+            line="952"
             column="12"/>
     </issue>
 
@@ -3077,7 +1955,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="998"
+            line="988"
             column="12"/>
     </issue>
 
@@ -3088,7 +1966,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1009"
+            line="999"
             column="43"/>
     </issue>
 
@@ -3099,7 +1977,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1022"
+            line="1012"
             column="46"/>
     </issue>
 
@@ -3110,7 +1988,7 @@
         errorLine2="                  ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1045"
+            line="1035"
             column="19"/>
     </issue>
 
@@ -3121,7 +1999,7 @@
         errorLine2="                                                      ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1045"
+            line="1035"
             column="55"/>
     </issue>
 
@@ -3132,7 +2010,7 @@
         errorLine2="                                                                       ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1045"
+            line="1035"
             column="72"/>
     </issue>
 
@@ -3143,7 +2021,7 @@
         errorLine2="                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1178"
+            line="1168"
             column="31"/>
     </issue>
 
@@ -3154,7 +2032,7 @@
         errorLine2="                                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1178"
+            line="1168"
             column="47"/>
     </issue>
 
@@ -3165,7 +2043,7 @@
         errorLine2="                                                             ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1178"
+            line="1168"
             column="62"/>
     </issue>
 
@@ -3176,7 +2054,7 @@
         errorLine2="                                          ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1192"
+            line="1182"
             column="43"/>
     </issue>
 
@@ -3187,7 +2065,7 @@
         errorLine2="                                         ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1285"
+            line="1275"
             column="42"/>
     </issue>
 
@@ -3198,7 +2076,7 @@
         errorLine2="                                                         ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1285"
+            line="1275"
             column="58"/>
     </issue>
 
@@ -3209,7 +2087,7 @@
         errorLine2="                                        ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1295"
+            line="1285"
             column="41"/>
     </issue>
 
@@ -3220,7 +2098,7 @@
         errorLine2="                                                      ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1295"
+            line="1285"
             column="55"/>
     </issue>
 
@@ -3231,7 +2109,7 @@
         errorLine2="                                     ~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1303"
+            line="1293"
             column="38"/>
     </issue>
 
@@ -3242,7 +2120,7 @@
         errorLine2="                                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1303"
+            line="1293"
             column="47"/>
     </issue>
 
@@ -3253,7 +2131,7 @@
         errorLine2="                                      ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1316"
+            line="1306"
             column="39"/>
     </issue>
 
@@ -3264,7 +2142,7 @@
         errorLine2="                                                      ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1316"
+            line="1306"
             column="55"/>
     </issue>
 
@@ -3275,7 +2153,7 @@
         errorLine2="                                     ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1325"
+            line="1315"
             column="38"/>
     </issue>
 
@@ -3286,7 +2164,7 @@
         errorLine2="                                                   ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1325"
+            line="1315"
             column="52"/>
     </issue>
 
@@ -3297,7 +2175,7 @@
         errorLine2="                                  ~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1331"
+            line="1321"
             column="35"/>
     </issue>
 
@@ -3308,7 +2186,7 @@
         errorLine2="                                           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1331"
+            line="1321"
             column="44"/>
     </issue>
 
@@ -3319,7 +2197,7 @@
         errorLine2="                                ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1390"
+            line="1380"
             column="33"/>
     </issue>
 
@@ -3330,7 +2208,7 @@
         errorLine2="                                ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1399"
+            line="1389"
             column="33"/>
     </issue>
 
@@ -3341,7 +2219,7 @@
         errorLine2="                                                     ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1399"
+            line="1389"
             column="54"/>
     </issue>
 
@@ -3352,7 +2230,7 @@
         errorLine2="                                   ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1470"
+            line="1460"
             column="36"/>
     </issue>
 
@@ -3363,7 +2241,7 @@
         errorLine2="                                                  ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1470"
+            line="1460"
             column="51"/>
     </issue>
 
@@ -3374,7 +2252,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1480"
+            line="1470"
             column="36"/>
     </issue>
 
@@ -3385,7 +2263,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1492"
+            line="1482"
             column="36"/>
     </issue>
 
@@ -3396,7 +2274,7 @@
         errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1503"
+            line="1493"
             column="39"/>
     </issue>
 
@@ -3407,7 +2285,7 @@
         errorLine2="                      ~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1964"
+            line="1954"
             column="23"/>
     </issue>
 
@@ -3418,7 +2296,7 @@
         errorLine2="                                      ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1964"
+            line="1954"
             column="39"/>
     </issue>
 
@@ -3429,7 +2307,7 @@
         errorLine2="                      ~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1982"
+            line="1972"
             column="23"/>
     </issue>
 
@@ -3440,7 +2318,7 @@
         errorLine2="                                      ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1982"
+            line="1972"
             column="39"/>
     </issue>
 
@@ -3451,7 +2329,7 @@
         errorLine2="                                                    ~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1982"
+            line="1972"
             column="53"/>
     </issue>
 
@@ -3462,7 +2340,7 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="1999"
+            line="1989"
             column="35"/>
     </issue>
 
@@ -3473,7 +2351,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2043"
+            line="2033"
             column="16"/>
     </issue>
 
@@ -3484,7 +2362,7 @@
         errorLine2="               ~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2051"
+            line="2041"
             column="16"/>
     </issue>
 
@@ -3495,7 +2373,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2061"
+            line="2051"
             column="36"/>
     </issue>
 
@@ -3506,7 +2384,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2071"
+            line="2061"
             column="16"/>
     </issue>
 
@@ -3517,7 +2395,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2081"
+            line="2071"
             column="38"/>
     </issue>
 
@@ -3528,7 +2406,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2091"
+            line="2081"
             column="16"/>
     </issue>
 
@@ -3539,7 +2417,7 @@
         errorLine2="                      ~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2113"
+            line="2103"
             column="23"/>
     </issue>
 
@@ -3550,7 +2428,7 @@
         errorLine2="                                       ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2113"
+            line="2103"
             column="40"/>
     </issue>
 
@@ -3561,7 +2439,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2168"
+            line="2158"
             column="26"/>
     </issue>
 
@@ -3572,7 +2450,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2195"
+            line="2185"
             column="16"/>
     </issue>
 
@@ -3583,7 +2461,7 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2207"
+            line="2197"
             column="35"/>
     </issue>
 
@@ -3594,7 +2472,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2227"
+            line="2217"
             column="16"/>
     </issue>
 
@@ -3605,7 +2483,7 @@
         errorLine2="                      ~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2247"
+            line="2237"
             column="23"/>
     </issue>
 
@@ -3616,7 +2494,7 @@
         errorLine2="                                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2247"
+            line="2237"
             column="47"/>
     </issue>
 
@@ -3627,7 +2505,7 @@
         errorLine2="                      ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2269"
+            line="2259"
             column="23"/>
     </issue>
 
@@ -3638,7 +2516,7 @@
         errorLine2="                                                        ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
-            line="2269"
+            line="2259"
             column="57"/>
     </issue>
 
@@ -3660,7 +2538,7 @@
         errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="101"
+            line="102"
             column="23"/>
     </issue>
 
@@ -3671,7 +2549,7 @@
         errorLine2="                                                               ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="101"
+            line="102"
             column="64"/>
     </issue>
 
@@ -3682,7 +2560,7 @@
         errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="138"
+            line="139"
             column="27"/>
     </issue>
 
@@ -3693,7 +2571,7 @@
         errorLine2="               ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="148"
+            line="149"
             column="16"/>
     </issue>
 
@@ -3704,7 +2582,7 @@
         errorLine2="                                                      ~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="148"
+            line="149"
             column="55"/>
     </issue>
 
@@ -3715,7 +2593,7 @@
         errorLine2="               ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="157"
+            line="158"
             column="16"/>
     </issue>
 
@@ -3726,7 +2604,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="157"
+            line="158"
             column="43"/>
     </issue>
 
@@ -3737,7 +2615,7 @@
         errorLine2="               ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="186"
+            line="187"
             column="16"/>
     </issue>
 
@@ -3748,7 +2626,7 @@
         errorLine2="               ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="199"
+            line="200"
             column="16"/>
     </issue>
 
@@ -3759,7 +2637,7 @@
         errorLine2="                                                ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="199"
+            line="200"
             column="49"/>
     </issue>
 
@@ -3770,7 +2648,7 @@
         errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="209"
+            line="210"
             column="27"/>
     </issue>
 
@@ -3781,7 +2659,7 @@
         errorLine2="               ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="234"
+            line="235"
             column="16"/>
     </issue>
 
@@ -3792,7 +2670,7 @@
         errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="234"
+            line="235"
             column="44"/>
     </issue>
 
@@ -3803,7 +2681,7 @@
         errorLine2="               ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="302"
+            line="303"
             column="16"/>
     </issue>
 
@@ -3814,7 +2692,7 @@
         errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="302"
+            line="303"
             column="47"/>
     </issue>
 
@@ -3825,7 +2703,7 @@
         errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="387"
+            line="388"
             column="27"/>
     </issue>
 
@@ -3836,7 +2714,7 @@
         errorLine2="               ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="401"
+            line="402"
             column="16"/>
     </issue>
 
@@ -3847,7 +2725,7 @@
         errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="401"
+            line="402"
             column="44"/>
     </issue>
 
@@ -3858,7 +2736,7 @@
         errorLine2="               ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="443"
+            line="444"
             column="16"/>
     </issue>
 
@@ -3869,7 +2747,7 @@
         errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="443"
+            line="444"
             column="47"/>
     </issue>
 
@@ -3880,7 +2758,7 @@
         errorLine2="               ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="475"
+            line="476"
             column="16"/>
     </issue>
 
@@ -3891,7 +2769,7 @@
         errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/app/NotificationCompat.java"
-            line="475"
+            line="476"
             column="51"/>
     </issue>
 
@@ -3924,7 +2802,7 @@
         errorLine2="                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="616"
+            line="618"
             column="31"/>
     </issue>
 
@@ -3935,7 +2813,7 @@
         errorLine2="                                   ~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="677"
+            line="679"
             column="36"/>
     </issue>
 
@@ -3946,7 +2824,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="739"
+            line="741"
             column="12"/>
     </issue>
 
@@ -3957,7 +2835,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="772"
+            line="774"
             column="12"/>
     </issue>
 
@@ -3968,7 +2846,7 @@
         errorLine2="                  ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="806"
+            line="808"
             column="19"/>
     </issue>
 
@@ -3979,7 +2857,7 @@
         errorLine2="                                                        ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="806"
+            line="808"
             column="57"/>
     </issue>
 
@@ -3990,7 +2868,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="851"
+            line="854"
             column="12"/>
     </issue>
 
@@ -4001,7 +2879,7 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="915"
+            line="918"
             column="35"/>
     </issue>
 
@@ -4012,7 +2890,7 @@
         errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="938"
+            line="941"
             column="23"/>
     </issue>
 
@@ -4023,7 +2901,7 @@
         errorLine2="                                                                        ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="938"
+            line="941"
             column="73"/>
     </issue>
 
@@ -4034,7 +2912,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="967"
+            line="970"
             column="16"/>
     </issue>
 
@@ -4045,7 +2923,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="997"
+            line="1000"
             column="16"/>
     </issue>
 
@@ -4056,7 +2934,7 @@
         errorLine2="               ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1006"
+            line="1009"
             column="16"/>
     </issue>
 
@@ -4067,7 +2945,7 @@
         errorLine2="               ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1029"
+            line="1032"
             column="16"/>
     </issue>
 
@@ -4078,7 +2956,7 @@
         errorLine2="                           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1063"
+            line="1066"
             column="28"/>
     </issue>
 
@@ -4089,7 +2967,7 @@
         errorLine2="                                          ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1063"
+            line="1066"
             column="43"/>
     </issue>
 
@@ -4100,7 +2978,7 @@
         errorLine2="                   ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1090"
+            line="1093"
             column="20"/>
     </issue>
 
@@ -4111,7 +2989,7 @@
         errorLine2="                                     ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1090"
+            line="1093"
             column="38"/>
     </issue>
 
@@ -4122,7 +3000,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1101"
+            line="1104"
             column="20"/>
     </issue>
 
@@ -4133,7 +3011,7 @@
         errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1136"
+            line="1139"
             column="24"/>
     </issue>
 
@@ -4144,7 +3022,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1183"
+            line="1186"
             column="16"/>
     </issue>
 
@@ -4155,7 +3033,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1221"
+            line="1224"
             column="16"/>
     </issue>
 
@@ -4166,7 +3044,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1237"
+            line="1240"
             column="16"/>
     </issue>
 
@@ -4177,7 +3055,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1272"
+            line="1275"
             column="16"/>
     </issue>
 
@@ -4188,7 +3066,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1297"
+            line="1300"
             column="16"/>
     </issue>
 
@@ -4199,7 +3077,7 @@
         errorLine2="                                       ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1297"
+            line="1300"
             column="40"/>
     </issue>
 
@@ -4210,7 +3088,7 @@
         errorLine2="                                                      ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1297"
+            line="1300"
             column="55"/>
     </issue>
 
@@ -4221,7 +3099,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1312"
+            line="1315"
             column="16"/>
     </issue>
 
@@ -4232,7 +3110,7 @@
         errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1312"
+            line="1315"
             column="40"/>
     </issue>
 
@@ -4243,7 +3121,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1328"
+            line="1331"
             column="16"/>
     </issue>
 
@@ -4254,7 +3132,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1341"
+            line="1344"
             column="16"/>
     </issue>
 
@@ -4265,7 +3143,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1341"
+            line="1344"
             column="40"/>
     </issue>
 
@@ -4276,7 +3154,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1354"
+            line="1357"
             column="16"/>
     </issue>
 
@@ -4287,7 +3165,7 @@
         errorLine2="                                                                 ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1354"
+            line="1357"
             column="66"/>
     </issue>
 
@@ -4298,7 +3176,7 @@
         errorLine2="               ~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1366"
+            line="1369"
             column="16"/>
     </issue>
 
@@ -4309,7 +3187,7 @@
         errorLine2="                                 ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1366"
+            line="1369"
             column="34"/>
     </issue>
 
@@ -4320,7 +3198,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
-            line="1374"
+            line="1377"
             column="16"/>
     </issue>
 
@@ -4331,7 +3209,7 @@
         errorLine2="                              ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="126"
+            line="128"
             column="31"/>
     </issue>
 
@@ -4342,7 +3220,7 @@
         errorLine2="                  ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="158"
+            line="160"
             column="19"/>
     </issue>
 
@@ -4353,7 +3231,7 @@
         errorLine2="                  ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="179"
+            line="181"
             column="19"/>
     </issue>
 
@@ -4364,7 +3242,7 @@
         errorLine2="                  ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="190"
+            line="192"
             column="19"/>
     </issue>
 
@@ -4375,7 +3253,7 @@
         errorLine2="                  ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="206"
+            line="208"
             column="19"/>
     </issue>
 
@@ -4386,7 +3264,7 @@
         errorLine2="                  ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="237"
+            line="239"
             column="19"/>
     </issue>
 
@@ -4397,7 +3275,7 @@
         errorLine2="                  ~~~~~~~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="332"
+            line="334"
             column="19"/>
     </issue>
 
@@ -4408,7 +3286,7 @@
         errorLine2="                                          ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="332"
+            line="334"
             column="43"/>
     </issue>
 
@@ -4419,7 +3297,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/android/support/v4/media/RatingCompat.java"
-            line="375"
+            line="377"
             column="12"/>
     </issue>
 
@@ -4430,7 +3308,7 @@
         errorLine2="                            ~~~~~~~~">
         <location
             file="src/main/java/androidx/media/VolumeProviderCompat.java"
-            line="188"
+            line="190"
             column="29"/>
     </issue>
 
@@ -4441,7 +3319,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/androidx/media/VolumeProviderCompat.java"
-            line="200"
+            line="202"
             column="12"/>
     </issue>
 
@@ -4452,7 +3330,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/media/VolumeProviderCompat.java"
-            line="236"
+            line="238"
             column="46"/>
     </issue>
 
diff --git a/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java b/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
index c9aa019..fafb972 100644
--- a/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
@@ -55,6 +55,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.media.MediaDescription;
 import android.media.browse.MediaBrowser;
 import android.os.BadParcelableException;
 import android.os.Binder;
@@ -75,6 +76,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -496,9 +498,9 @@
                 return null;
             }
             MediaBrowser.MediaItem itemFwk = (MediaBrowser.MediaItem) itemObj;
-            int flags = itemFwk.getFlags();
+            int flags = Api21Impl.getFlags(itemFwk);
             MediaDescriptionCompat descriptionCompat =
-                    MediaDescriptionCompat.fromMediaDescription(itemFwk.getDescription());
+                    MediaDescriptionCompat.fromMediaDescription(Api21Impl.getDescription(itemFwk));
             return new MediaItem(descriptionCompat, flags);
         }
 
@@ -2364,4 +2366,19 @@
             }
         }
     }
+
+    @RequiresApi(21)
+    private static class Api21Impl {
+        private Api21Impl() {}
+
+        @DoNotInline
+        static MediaDescription getDescription(MediaBrowser.MediaItem item) {
+            return item.getDescription();
+        }
+
+        @DoNotInline
+        static int getFlags(MediaBrowser.MediaItem item) {
+            return item.getFlags();
+        }
+    }
 }
diff --git a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
index 07b3dcc..49628eb 100644
--- a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
@@ -28,7 +28,9 @@
 import android.support.v4.media.session.MediaSessionCompat;
 import android.text.TextUtils;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
 /**
@@ -336,13 +338,13 @@
         if (mDescriptionFwk != null || Build.VERSION.SDK_INT < 21) {
             return mDescriptionFwk;
         }
-        MediaDescription.Builder bob = new MediaDescription.Builder();
-        bob.setMediaId(mMediaId);
-        bob.setTitle(mTitle);
-        bob.setSubtitle(mSubtitle);
-        bob.setDescription(mDescription);
-        bob.setIconBitmap(mIcon);
-        bob.setIconUri(mIconUri);
+        MediaDescription.Builder bob = Api21Impl.createBuilder();
+        Api21Impl.setMediaId(bob, mMediaId);
+        Api21Impl.setTitle(bob, mTitle);
+        Api21Impl.setSubtitle(bob, mSubtitle);
+        Api21Impl.setDescription(bob, mDescription);
+        Api21Impl.setIconBitmap(bob, mIcon);
+        Api21Impl.setIconUri(bob, mIconUri);
         // Media URI was not added until API 23, so add it to the Bundle of extras to
         // ensure the data is not lost - this ensures that
         // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) returns
@@ -355,11 +357,11 @@
             }
             extras.putParcelable(DESCRIPTION_KEY_MEDIA_URI, mMediaUri);
         }
-        bob.setExtras(extras);
+        Api21Impl.setExtras(bob, extras);
         if (Build.VERSION.SDK_INT >= 23) {
-            bob.setMediaUri(mMediaUri);
+            Api23Impl.setMediaUri(bob, mMediaUri);
         }
-        mDescriptionFwk = bob.build();
+        mDescriptionFwk = Api21Impl.build(bob);
 
         return mDescriptionFwk;
     }
@@ -380,13 +382,13 @@
         if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {
             Builder bob = new Builder();
             MediaDescription description = (MediaDescription) descriptionObj;
-            bob.setMediaId(description.getMediaId());
-            bob.setTitle(description.getTitle());
-            bob.setSubtitle(description.getSubtitle());
-            bob.setDescription(description.getDescription());
-            bob.setIconBitmap(description.getIconBitmap());
-            bob.setIconUri(description.getIconUri());
-            Bundle extras = description.getExtras();
+            bob.setMediaId(Api21Impl.getMediaId(description));
+            bob.setTitle(Api21Impl.getTitle(description));
+            bob.setSubtitle(Api21Impl.getSubtitle(description));
+            bob.setDescription(Api21Impl.getDescription(description));
+            bob.setIconBitmap(Api21Impl.getIconBitmap(description));
+            bob.setIconUri(Api21Impl.getIconUri(description));
+            Bundle extras = Api21Impl.getExtras(description);
             if (extras != null) {
                 extras = MediaSessionCompat.unparcelWithClassLoader(extras);
             }
@@ -412,7 +414,7 @@
             if (mediaUri != null) {
                 bob.setMediaUri(mediaUri);
             } else if (Build.VERSION.SDK_INT >= 23) {
-                bob.setMediaUri(description.getMediaUri());
+                bob.setMediaUri(Api23Impl.getMediaUri(description));
             }
             MediaDescriptionCompat descriptionCompat = bob.build();
             descriptionCompat.mDescriptionFwk = description;
@@ -561,4 +563,120 @@
                     mIconUri, mExtras, mMediaUri);
         }
     }
+
+    @RequiresApi(21)
+    private static class Api21Impl {
+        private Api21Impl() {}
+
+        @DoNotInline
+        static MediaDescription.Builder createBuilder() {
+            return new MediaDescription.Builder();
+        }
+
+        @DoNotInline
+        static void setMediaId(MediaDescription.Builder builder,
+                @Nullable String mediaId) {
+            builder.setMediaId(mediaId);
+        }
+
+        @DoNotInline
+        static void setTitle(MediaDescription.Builder builder,
+                @Nullable CharSequence title) {
+            builder.setTitle(title);
+        }
+
+        @DoNotInline
+        static void setSubtitle(MediaDescription.Builder builder,
+                @Nullable CharSequence subtitle) {
+            builder.setSubtitle(subtitle);
+        }
+
+        @DoNotInline
+        static void setDescription(MediaDescription.Builder builder,
+                @Nullable CharSequence description) {
+            builder.setDescription(description);
+        }
+
+        @DoNotInline
+        static void setIconBitmap(MediaDescription.Builder builder,
+                @Nullable Bitmap icon) {
+            builder.setIconBitmap(icon);
+        }
+
+        @DoNotInline
+        static void setIconUri(MediaDescription.Builder builder,
+                @Nullable Uri iconUri) {
+            builder.setIconUri(iconUri);
+        }
+
+        @DoNotInline
+        static void setExtras(MediaDescription.Builder builder,
+                @Nullable Bundle extras) {
+            builder.setExtras(extras);
+        }
+
+        @DoNotInline
+        static MediaDescription build(MediaDescription.Builder builder) {
+            return builder.build();
+        }
+
+        @DoNotInline
+        @Nullable
+        static String getMediaId(MediaDescription description) {
+            return description.getMediaId();
+        }
+
+        @DoNotInline
+        @Nullable
+        static CharSequence getTitle(MediaDescription description) {
+            return description.getTitle();
+        }
+
+        @DoNotInline
+        @Nullable
+        static CharSequence getSubtitle(MediaDescription description) {
+            return description.getSubtitle();
+        }
+
+        @DoNotInline
+        @Nullable
+        static CharSequence getDescription(MediaDescription description) {
+            return description.getDescription();
+        }
+
+        @DoNotInline
+        @Nullable
+        static Bitmap getIconBitmap(MediaDescription description) {
+            return description.getIconBitmap();
+        }
+
+        @DoNotInline
+        @Nullable
+        static Uri getIconUri(MediaDescription description) {
+            return description.getIconUri();
+        }
+
+        @DoNotInline
+        @Nullable
+        static Bundle getExtras(MediaDescription description) {
+            return description.getExtras();
+        }
+    }
+
+    @RequiresApi(23)
+    private static class Api23Impl {
+        private Api23Impl() {}
+
+        @DoNotInline
+        static void setMediaUri(MediaDescription.Builder builder,
+                @Nullable Uri mediaUri) {
+            builder.setMediaUri(mediaUri);
+        }
+
+        @DoNotInline
+        @Nullable
+        static Uri getMediaUri(MediaDescription description) {
+            return description.getMediaUri();
+        }
+    }
 }
diff --git a/media/media/src/main/java/android/support/v4/media/RatingCompat.java b/media/media/src/main/java/android/support/v4/media/RatingCompat.java
index 42b6da0..6578a65 100644
--- a/media/media/src/main/java/android/support/v4/media/RatingCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/RatingCompat.java
@@ -26,7 +26,9 @@
 import android.os.Parcelable;
 import android.util.Log;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
 import java.lang.annotation.Retention;
@@ -331,25 +333,25 @@
      */
     public static RatingCompat fromRating(Object ratingObj) {
         if (ratingObj != null && Build.VERSION.SDK_INT >= 19) {
-            final int ratingStyle = ((Rating) ratingObj).getRatingStyle();
+            final int ratingStyle = Api19Impl.getRatingStyle((Rating) ratingObj);
             final RatingCompat rating;
-            if (((Rating) ratingObj).isRated()) {
+            if (Api19Impl.isRated((Rating) ratingObj)) {
                 switch (ratingStyle) {
                     case RATING_HEART:
-                        rating = newHeartRating(((Rating) ratingObj).hasHeart());
+                        rating = newHeartRating(Api19Impl.hasHeart((Rating) ratingObj));
                         break;
                     case RATING_THUMB_UP_DOWN:
-                        rating = newThumbRating(((Rating) ratingObj).isThumbUp());
+                        rating = newThumbRating(Api19Impl.isThumbUp((Rating) ratingObj));
                         break;
                     case RATING_3_STARS:
                     case RATING_4_STARS:
                     case RATING_5_STARS:
                         rating = newStarRating(ratingStyle,
-                                ((Rating) ratingObj).getStarRating());
+                                Api19Impl.getStarRating((Rating) ratingObj));
                         break;
                     case RATING_PERCENTAGE:
                         rating = newPercentageRating(
-                                ((Rating) ratingObj).getPercentRating());
+                                Api19Impl.getPercentRating((Rating) ratingObj));
                         break;
                     default:
                         return null;
@@ -377,27 +379,87 @@
             if (isRated()) {
                 switch (mRatingStyle) {
                     case RATING_HEART:
-                        mRatingObj = Rating.newHeartRating(hasHeart());
+                        mRatingObj = Api19Impl.newHeartRating(hasHeart());
                         break;
                     case RATING_THUMB_UP_DOWN:
-                        mRatingObj = Rating.newThumbRating(isThumbUp());
+                        mRatingObj = Api19Impl.newThumbRating(isThumbUp());
                         break;
                     case RATING_3_STARS:
                     case RATING_4_STARS:
                     case RATING_5_STARS:
-                        mRatingObj = Rating.newStarRating(mRatingStyle,
+                        mRatingObj = Api19Impl.newStarRating(mRatingStyle,
                                 getStarRating());
                         break;
                     case RATING_PERCENTAGE:
-                        mRatingObj = Rating.newPercentageRating(getPercentRating());
+                        mRatingObj = Api19Impl.newPercentageRating(getPercentRating());
                         break;
                     default:
                         return null;
                 }
             } else {
-                mRatingObj = Rating.newUnratedRating(mRatingStyle);
+                mRatingObj = Api19Impl.newUnratedRating(mRatingStyle);
             }
         }
         return mRatingObj;
     }
+
+    @RequiresApi(19)
+    private static class Api19Impl {
+        private Api19Impl() {}
+
+        @DoNotInline
+        static int getRatingStyle(Rating rating) {
+            return rating.getRatingStyle();
+        }
+
+        @DoNotInline
+        static boolean isRated(Rating rating) {
+            return rating.isRated();
+        }
+
+        @DoNotInline
+        static boolean hasHeart(Rating rating) {
+            return rating.hasHeart();
+        }
+
+        @DoNotInline
+        static boolean isThumbUp(Rating rating) {
+            return rating.isThumbUp();
+        }
+
+        @DoNotInline
+        static float getStarRating(Rating rating) {
+            return rating.getStarRating();
+        }
+
+        @DoNotInline
+        static float getPercentRating(Rating rating) {
+            return rating.getPercentRating();
+        }
+
+        @DoNotInline
+        static Rating newHeartRating(boolean hasHeart) {
+            return Rating.newHeartRating(hasHeart);
+        }
+
+        @DoNotInline
+        static Rating newThumbRating(boolean thumbIsUp) {
+            return Rating.newThumbRating(thumbIsUp);
+        }
+
+        @DoNotInline
+        static Rating newStarRating(int starRatingStyle, float starRating) {
+            return Rating.newStarRating(starRatingStyle, starRating);
+        }
+
+        @DoNotInline
+        static Rating newPercentageRating(float percent) {
+            return Rating.newPercentageRating(percent);
+        }
+
+        @DoNotInline
+        static Rating newUnratedRating(int ratingStyle) {
+            return Rating.newUnratedRating(ratingStyle);
+        }
+    }
 }
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index dfe730b..7f71344 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -163,12 +163,7 @@
         activity.getWindow().getDecorView().setTag(
                 R.id.media_controller_compat_view_tag, mediaController);
         if (android.os.Build.VERSION.SDK_INT >= 21) {
-            MediaController controllerFwk = null;
-            if (mediaController != null) {
-                Object sessionTokenObj = mediaController.getSessionToken().getToken();
-                controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);
-            }
-            activity.setMediaController(controllerFwk);
+            MediaControllerImplApi21.setMediaController(activity, mediaController);
         }
     }
 
@@ -189,13 +184,7 @@
         if (tag instanceof MediaControllerCompat) {
             return (MediaControllerCompat) tag;
         } else if (android.os.Build.VERSION.SDK_INT >= 21) {
-            MediaController controllerFwk = activity.getMediaController();
-            if (controllerFwk == null) {
-                return null;
-            }
-            MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();
-            return new MediaControllerCompat(activity,
-                    MediaSessionCompat.Token.fromToken(sessionTokenFwk));
+            return MediaControllerImplApi21.getMediaController(activity);
         }
         return null;
     }
@@ -238,7 +227,9 @@
         }
         mToken = session.getSessionToken();
 
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
+        if (Build.VERSION.SDK_INT >= 29) {
+            mImpl = new MediaControllerImplApi29(context, mToken);
+        } else if (Build.VERSION.SDK_INT >= 21) {
             mImpl = new MediaControllerImplApi21(context, mToken);
         } else {
             mImpl = new MediaControllerImplBase(mToken);
@@ -2027,7 +2018,7 @@
 
         private HashMap<Callback, ExtraCallback> mCallbackMap = new HashMap<>();
 
-        private Bundle mSessionInfo;
+        protected Bundle mSessionInfo;
 
         final MediaSessionCompat.Token mSessionToken;
 
@@ -2090,7 +2081,16 @@
 
         @Override
         public TransportControls getTransportControls() {
-            return new TransportControlsApi21(mControllerFwk.getTransportControls());
+            MediaController.TransportControls controlsFwk = mControllerFwk.getTransportControls();
+            if (Build.VERSION.SDK_INT >= 29) {
+                return new TransportControlsApi29(controlsFwk);
+            } else if (Build.VERSION.SDK_INT >= 24) {
+                return new TransportControlsApi24(controlsFwk);
+            } else if (Build.VERSION.SDK_INT >= 23) {
+                return new TransportControlsApi23(controlsFwk);
+            } else {
+                return new TransportControlsApi21(controlsFwk);
+            }
         }
 
         @Override
@@ -2265,10 +2265,7 @@
                 return new Bundle(mSessionInfo);
             }
 
-            // Get the info from the connected session.
-            if (Build.VERSION.SDK_INT >= 29) {
-                mSessionInfo = mControllerFwk.getSessionInfo();
-            } else if (mSessionToken.getExtraBinder() != null) {
+            if (mSessionToken.getExtraBinder() != null) {
                 try {
                     mSessionInfo = mSessionToken.getExtraBinder().getSessionInfo();
                 } catch (RemoteException e) {
@@ -2310,6 +2307,27 @@
             mPendingCallbacks.clear();
         }
 
+        static void setMediaController(@NonNull Activity activity,
+                @Nullable MediaControllerCompat mediaControllerCompat) {
+            MediaController controllerFwk = null;
+            if (mediaControllerCompat != null) {
+                Object sessionTokenObj = mediaControllerCompat.getSessionToken().getToken();
+                controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);
+            }
+            activity.setMediaController(controllerFwk);
+        }
+
+        @Nullable
+        static MediaControllerCompat getMediaController(@NonNull Activity activity) {
+            MediaController controllerFwk = activity.getMediaController();
+            if (controllerFwk == null) {
+                return null;
+            }
+            MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();
+            return new MediaControllerCompat(activity,
+                    MediaSessionCompat.Token.fromToken(sessionTokenFwk));
+        }
+
         private static class ExtraBinderRequestResultReceiver extends ResultReceiver {
             private WeakReference<MediaControllerImplApi21> mMediaControllerImpl;
 
@@ -2380,6 +2398,23 @@
         }
     }
 
+    @RequiresApi(29)
+    static class MediaControllerImplApi29 extends MediaControllerImplApi21 {
+        MediaControllerImplApi29(Context context, MediaSessionCompat.Token sessionToken) {
+            super(context, sessionToken);
+        }
+
+        @Override
+        public Bundle getSessionInfo() {
+            if (mSessionInfo != null) {
+                return new Bundle(mSessionInfo);
+            }
+            mSessionInfo = mControllerFwk.getSessionInfo();
+            mSessionInfo = MediaSessionCompat.unparcelWithClassLoader(mSessionInfo);
+            return mSessionInfo == null ? Bundle.EMPTY : new Bundle(mSessionInfo);
+        }
+    }
+
     @RequiresApi(21)
     static class TransportControlsApi21 extends TransportControls {
         protected final MediaController.TransportControls mControlsFwk;
@@ -2390,19 +2425,11 @@
 
         @Override
         public void prepare() {
-            if (Build.VERSION.SDK_INT >= 24) {
-                mControlsFwk.prepare();
-                return;
-            }
             sendCustomAction(MediaSessionCompat.ACTION_PREPARE, null);
         }
 
         @Override
         public void prepareFromMediaId(String mediaId, Bundle extras) {
-            if (Build.VERSION.SDK_INT >= 24) {
-                mControlsFwk.prepareFromMediaId(mediaId, extras);
-                return;
-            }
             Bundle bundle = new Bundle();
             bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_MEDIA_ID, mediaId);
             bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
@@ -2411,10 +2438,6 @@
 
         @Override
         public void prepareFromSearch(String query, Bundle extras) {
-            if (Build.VERSION.SDK_INT >= 24) {
-                mControlsFwk.prepareFromSearch(query, extras);
-                return;
-            }
             Bundle bundle = new Bundle();
             bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_QUERY, query);
             bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
@@ -2423,10 +2446,6 @@
 
         @Override
         public void prepareFromUri(Uri uri, Bundle extras) {
-            if (Build.VERSION.SDK_INT >= 24) {
-                mControlsFwk.prepareFromUri(uri, extras);
-                return;
-            }
             Bundle bundle = new Bundle();
             bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_URI, uri);
             bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
@@ -2491,10 +2510,6 @@
             if (speed == 0.0f) {
                 throw new IllegalArgumentException("speed must not be zero");
             }
-            if (Build.VERSION.SDK_INT >= 29) {
-                mControlsFwk.setPlaybackSpeed(speed);
-                return;
-            }
             Bundle bundle = new Bundle();
             bundle.putFloat(MediaSessionCompat.ACTION_ARGUMENT_PLAYBACK_SPEED, speed);
             sendCustomAction(MediaSessionCompat.ACTION_SET_PLAYBACK_SPEED, bundle);
@@ -2533,10 +2548,6 @@
 
         @Override
         public void playFromUri(Uri uri, Bundle extras) {
-            if (Build.VERSION.SDK_INT >= 23) {
-                mControlsFwk.playFromUri(uri, extras);
-                return;
-            }
             if (uri == null || Uri.EMPTY.equals(uri)) {
                 throw new IllegalArgumentException(
                         "You must specify a non-empty Uri for playFromUri.");
@@ -2564,4 +2575,58 @@
             mControlsFwk.sendCustomAction(action, args);
         }
     }
+
+    @RequiresApi(23)
+    static class TransportControlsApi23 extends TransportControlsApi21 {
+        TransportControlsApi23(MediaController.TransportControls controlsFwk) {
+            super(controlsFwk);
+        }
+
+        @Override
+        public void playFromUri(Uri uri, Bundle extras) {
+            mControlsFwk.playFromUri(uri, extras);
+        }
+    }
+
+    @RequiresApi(24)
+    static class TransportControlsApi24 extends TransportControlsApi23 {
+        TransportControlsApi24(MediaController.TransportControls controlsFwk) {
+            super(controlsFwk);
+        }
+
+        @Override
+        public void prepare() {
+            mControlsFwk.prepare();
+        }
+
+        @Override
+        public void prepareFromMediaId(String mediaId, Bundle extras) {
+            mControlsFwk.prepareFromMediaId(mediaId, extras);
+        }
+
+        @Override
+        public void prepareFromSearch(String query, Bundle extras) {
+            mControlsFwk.prepareFromSearch(query, extras);
+        }
+
+        @Override
+        public void prepareFromUri(Uri uri, Bundle extras) {
+            mControlsFwk.prepareFromUri(uri, extras);
+        }
+    }
+
+    @RequiresApi(29)
+    static class TransportControlsApi29 extends TransportControlsApi24 {
+        TransportControlsApi29(MediaController.TransportControls controlsFwk) {
+            super(controlsFwk);
+        }
+
+        @Override
+        public void setPlaybackSpeed(float speed) {
+            if (speed == 0.0f) {
+                throw new IllegalArgumentException("speed must not be zero");
+            }
+            mControlsFwk.setPlaybackSpeed(speed);
+        }
+    }
 }
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
index 5518d39..5f0fe6b 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -65,6 +65,7 @@
 import android.view.KeyEvent;
 import android.view.ViewConfiguration;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -570,13 +571,14 @@
         }
 
         if (android.os.Build.VERSION.SDK_INT >= 21) {
-            MediaSession sessionFwk = createFwkMediaSession(context, tag, sessionInfo);
             if (android.os.Build.VERSION.SDK_INT >= 29) {
-                mImpl = new MediaSessionImplApi29(sessionFwk, session2Token, sessionInfo);
+                mImpl = new MediaSessionImplApi29(context, tag, session2Token, sessionInfo);
             } else if (android.os.Build.VERSION.SDK_INT >= 28) {
-                mImpl = new MediaSessionImplApi28(sessionFwk, session2Token, sessionInfo);
+                mImpl = new MediaSessionImplApi28(context, tag, session2Token, sessionInfo);
+            } else if (android.os.Build.VERSION.SDK_INT >= 22) {
+                mImpl = new MediaSessionImplApi22(context, tag, session2Token, sessionInfo);
             } else {
-                mImpl = new MediaSessionImplApi21(sessionFwk, session2Token, sessionInfo);
+                mImpl = new MediaSessionImplApi21(context, tag, session2Token, sessionInfo);
             }
             // Set default callback to respond to controllers' extra binder requests.
             Handler handler = new Handler(Looper.myLooper() != null
@@ -606,16 +608,6 @@
         mController = new MediaControllerCompat(context, this);
     }
 
-    @RequiresApi(21)
-    private MediaSession createFwkMediaSession(Context context, String tag,
-            Bundle sessionInfo) {
-        if (android.os.Build.VERSION.SDK_INT >= 29) {
-            return new MediaSession(context, tag, sessionInfo);
-        } else {
-            return new MediaSession(context, tag);
-        }
-    }
-
     /**
      * Adds a callback to receive updates on for the MediaSession. This includes
      * media button and volume events. The caller's thread will be used to post
@@ -2228,7 +2220,7 @@
             if (mItemFwk != null || android.os.Build.VERSION.SDK_INT < 21) {
                 return mItemFwk;
             }
-            mItemFwk = new MediaSession.QueueItem(
+            mItemFwk = Api21Impl.createQueueItem(
                     (MediaDescription) mDescription.getMediaDescription(),
                     mId);
             return mItemFwk;
@@ -2249,10 +2241,10 @@
                 return null;
             }
             MediaSession.QueueItem queueItemObj = (MediaSession.QueueItem) queueItem;
-            Object descriptionObj = queueItemObj.getDescription();
+            Object descriptionObj = Api21Impl.getDescription(queueItemObj);
             MediaDescriptionCompat description = MediaDescriptionCompat.fromMediaDescription(
                     descriptionObj);
-            long id = queueItemObj.getQueueId();
+            long id = Api21Impl.getQueueId(queueItemObj);
             return new QueueItem(queueItemObj, description, id);
         }
 
@@ -2297,6 +2289,26 @@
                     "Description=" + mDescription +
                     ", Id=" + mId + " }";
         }
+
+        @RequiresApi(21)
+        private static class Api21Impl {
+            private Api21Impl() {}
+
+            @DoNotInline
+            static MediaSession.QueueItem createQueueItem(MediaDescription description, long id) {
+                return new MediaSession.QueueItem(description, id);
+            }
+
+            @DoNotInline
+            static MediaDescription getDescription(MediaSession.QueueItem queueItem) {
+                return queueItem.getDescription();
+            }
+
+            @DoNotInline
+            static long getQueueId(MediaSession.QueueItem queueItem) {
+                return queueItem.getQueueId();
+            }
+        }
     }
 
     /**
@@ -3802,9 +3814,9 @@
         @GuardedBy("mLock")
         RemoteUserInfo mRemoteUserInfo;
 
-        MediaSessionImplApi21(MediaSession sessionFwk, VersionedParcelable session2Token,
+        MediaSessionImplApi21(Context context, String tag, VersionedParcelable session2Token,
                 Bundle sessionInfo) {
-            mSessionFwk = sessionFwk;
+            mSessionFwk = createFwkMediaSession(context, tag, sessionInfo);
             mToken = new Token(mSessionFwk.getSessionToken(), new ExtraSession(), session2Token);
             mSessionInfo = sessionInfo;
             // For backward compatibility, these flags are always set.
@@ -3823,6 +3835,10 @@
             setFlags(FLAG_HANDLES_MEDIA_BUTTONS | FLAG_HANDLES_TRANSPORT_CONTROLS);
         }
 
+        public MediaSession createFwkMediaSession(Context context, String tag, Bundle sessionInfo) {
+            return new MediaSession(context, tag);
+        }
+
         @Override
         public void setCallback(Callback callback, Handler handler) {
             synchronized (mLock) {
@@ -3968,11 +3984,7 @@
 
         @Override
         public void setRatingType(@RatingCompat.Style int type) {
-            if (android.os.Build.VERSION.SDK_INT < 22) {
-                mRatingType = type;
-            } else {
-                mSessionFwk.setRatingType(type);
-            }
+            mRatingType = type;
         }
 
         @Override
@@ -4384,11 +4396,28 @@
         }
     }
 
-    @RequiresApi(28)
-    static class MediaSessionImplApi28 extends MediaSessionImplApi21 {
-        MediaSessionImplApi28(MediaSession sessionFwk, VersionedParcelable session2Token,
+    @RequiresApi(22)
+    static class MediaSessionImplApi22 extends MediaSessionImplApi21 {
+        MediaSessionImplApi22(Context context, String tag, VersionedParcelable session2Token,
                 Bundle sessionInfo) {
-            super(sessionFwk, session2Token, sessionInfo);
+            super(context, tag, session2Token, sessionInfo);
+        }
+
+        MediaSessionImplApi22(Object mediaSession) {
+            super(mediaSession);
+        }
+
+        @Override
+        public void setRatingType(@RatingCompat.Style int type) {
+            mSessionFwk.setRatingType(type);
+        }
+    }
+
+    @RequiresApi(28)
+    static class MediaSessionImplApi28 extends MediaSessionImplApi22 {
+        MediaSessionImplApi28(Context context, String tag, VersionedParcelable session2Token,
+                Bundle sessionInfo) {
+            super(context, tag, session2Token, sessionInfo);
         }
 
         MediaSessionImplApi28(Object mediaSession) {
@@ -4411,14 +4440,19 @@
 
     @RequiresApi(29)
     static class MediaSessionImplApi29 extends MediaSessionImplApi28 {
-        MediaSessionImplApi29(MediaSession sessionFwk, VersionedParcelable session2Token,
+        MediaSessionImplApi29(Context context, String tag, VersionedParcelable session2Token,
                 Bundle sessionInfo) {
-            super(sessionFwk, session2Token, sessionInfo);
+            super(context, tag, session2Token, sessionInfo);
         }
 
         MediaSessionImplApi29(Object mediaSession) {
             super(mediaSession);
             mSessionInfo = ((MediaSession) mediaSession).getController().getSessionInfo();
         }
+
+        @Override
+        public MediaSession createFwkMediaSession(Context context, String tag, Bundle sessionInfo) {
+            return new MediaSession(context, tag, sessionInfo);
+        }
     }
 }
diff --git a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
index d51a003..1026438 100644
--- a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -28,9 +28,11 @@
 import android.text.TextUtils;
 import android.view.KeyEvent;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.LongDef;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
 import java.lang.annotation.Retention;
@@ -806,7 +808,8 @@
     public static PlaybackStateCompat fromPlaybackState(Object stateObj) {
         if (stateObj != null && Build.VERSION.SDK_INT >= 21) {
             PlaybackState stateFwk = (PlaybackState) stateObj;
-            List<PlaybackState.CustomAction> customActionFwks = stateFwk.getCustomActions();
+            List<PlaybackState.CustomAction> customActionFwks =
+                    Api21Impl.getCustomActions(stateFwk);
             List<PlaybackStateCompat.CustomAction> customActions = null;
             if (customActionFwks != null) {
                 customActions = new ArrayList<>(customActionFwks.size());
@@ -816,22 +819,22 @@
             }
             Bundle extras;
             if (Build.VERSION.SDK_INT >= 22) {
-                extras = stateFwk.getExtras();
+                extras = Api22Impl.getExtras(stateFwk);
                 MediaSessionCompat.ensureClassLoader(extras);
             } else {
                 extras = null;
             }
             PlaybackStateCompat stateCompat = new PlaybackStateCompat(
-                    stateFwk.getState(),
-                    stateFwk.getPosition(),
-                    stateFwk.getBufferedPosition(),
-                    stateFwk.getPlaybackSpeed(),
-                    stateFwk.getActions(),
+                    Api21Impl.getState(stateFwk),
+                    Api21Impl.getPosition(stateFwk),
+                    Api21Impl.getBufferedPosition(stateFwk),
+                    Api21Impl.getPlaybackSpeed(stateFwk),
+                    Api21Impl.getActions(stateFwk),
                     ERROR_CODE_UNKNOWN_ERROR,
-                    stateFwk.getErrorMessage(),
-                    stateFwk.getLastPositionUpdateTime(),
+                    Api21Impl.getErrorMessage(stateFwk),
+                    Api21Impl.getLastPositionUpdateTime(stateFwk),
                     customActions,
-                    stateFwk.getActiveQueueItemId(),
+                    Api21Impl.getActiveQueueItemId(stateFwk),
                     extras);
             stateCompat.mStateFwk = stateFwk;
             return stateCompat;
@@ -850,20 +853,20 @@
      */
     public Object getPlaybackState() {
         if (mStateFwk == null && Build.VERSION.SDK_INT >= 21) {
-            PlaybackState.Builder builder = new PlaybackState.Builder();
-            builder.setState(mState, mPosition, mSpeed, mUpdateTime);
-            builder.setBufferedPosition(mBufferedPosition);
-            builder.setActions(mActions);
-            builder.setErrorMessage(mErrorMessage);
+            PlaybackState.Builder builder = Api21Impl.createBuilder();
+            Api21Impl.setState(builder, mState, mPosition, mSpeed, mUpdateTime);
+            Api21Impl.setBufferedPosition(builder, mBufferedPosition);
+            Api21Impl.setActions(builder, mActions);
+            Api21Impl.setErrorMessage(builder, mErrorMessage);
             for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
-                builder.addCustomAction(
+                Api21Impl.addCustomAction(builder,
                         (PlaybackState.CustomAction) customAction.getCustomAction());
             }
-            builder.setActiveQueueItemId(mActiveItemId);
+            Api21Impl.setActiveQueueItemId(builder, mActiveItemId);
             if (Build.VERSION.SDK_INT >= 22) {
-                builder.setExtras(mExtras);
+                Api22Impl.setExtras(builder, mExtras);
             }
-            mStateFwk = builder.build();
+            mStateFwk = Api21Impl.build(builder);
         }
         return mStateFwk;
     }
@@ -942,13 +945,13 @@
 
             PlaybackState.CustomAction customActionFwk =
                     (PlaybackState.CustomAction) customActionObj;
-            Bundle extras = customActionFwk.getExtras();
+            Bundle extras = Api21Impl.getExtras(customActionFwk);
             MediaSessionCompat.ensureClassLoader(extras);
             PlaybackStateCompat.CustomAction customActionCompat =
                     new PlaybackStateCompat.CustomAction(
-                            customActionFwk.getAction(),
-                            customActionFwk.getName(),
-                            customActionFwk.getIcon(),
+                            Api21Impl.getAction(customActionFwk),
+                            Api21Impl.getName(customActionFwk),
+                            Api21Impl.getIcon(customActionFwk),
                             extras);
             customActionCompat.mCustomActionFwk = customActionFwk;
             return customActionCompat;
@@ -969,10 +972,10 @@
                 return mCustomActionFwk;
             }
 
-            PlaybackState.CustomAction.Builder builder = new PlaybackState.CustomAction.Builder(
-                    mAction, mName, mIcon);
-            builder.setExtras(mExtras);
-            return builder.build();
+            PlaybackState.CustomAction.Builder builder =
+                    Api21Impl.createCustomActionBuilder(mAction, mName, mIcon);
+            Api21Impl.setExtras(builder, mExtras);
+            return Api21Impl.build(builder);
         }
 
         public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR
@@ -1377,4 +1380,147 @@
                     mCustomActions, mActiveItemId, mExtras);
         }
     }
-}
+
+    @RequiresApi(21)
+    private static class Api21Impl {
+        private Api21Impl() {}
+
+        @DoNotInline
+        static PlaybackState.Builder createBuilder() {
+            return new PlaybackState.Builder();
+        }
+
+        @DoNotInline
+        static void setState(PlaybackState.Builder builder, int state, long position,
+                float playbackSpeed, long updateTime) {
+            builder.setState(state, position, playbackSpeed, updateTime);
+        }
+
+        @DoNotInline
+        static void setBufferedPosition(PlaybackState.Builder builder, long bufferedPosition) {
+            builder.setBufferedPosition(bufferedPosition);
+        }
+
+        @DoNotInline
+        static void setActions(PlaybackState.Builder builder, long actions) {
+            builder.setActions(actions);
+        }
+
+        @DoNotInline
+        static void setErrorMessage(PlaybackState.Builder builder, CharSequence error) {
+            builder.setErrorMessage(error);
+        }
+
+        @DoNotInline
+        static void addCustomAction(PlaybackState.Builder builder,
+                PlaybackState.CustomAction customAction) {
+            builder.addCustomAction(customAction);
+        }
+
+        @DoNotInline
+        static void setActiveQueueItemId(PlaybackState.Builder builder, long id) {
+            builder.setActiveQueueItemId(id);
+        }
+
+        @DoNotInline
+        static List<PlaybackState.CustomAction> getCustomActions(PlaybackState state) {
+            return state.getCustomActions();
+        }
+
+        @DoNotInline
+        static PlaybackState build(PlaybackState.Builder builder) {
+            return builder.build();
+        }
+
+        @DoNotInline
+        static int getState(PlaybackState state) {
+            return state.getState();
+        }
+
+        @DoNotInline
+        static long getPosition(PlaybackState state) {
+            return state.getPosition();
+        }
+
+        @DoNotInline
+        static long getBufferedPosition(PlaybackState state) {
+            return state.getBufferedPosition();
+        }
+
+        @DoNotInline
+        static float getPlaybackSpeed(PlaybackState state) {
+            return state.getPlaybackSpeed();
+        }
+
+        @DoNotInline
+        static long getActions(PlaybackState state) {
+            return state.getActions();
+        }
+
+        @DoNotInline
+        static CharSequence getErrorMessage(PlaybackState state) {
+            return state.getErrorMessage();
+        }
+
+        @DoNotInline
+        static long getLastPositionUpdateTime(PlaybackState state) {
+            return state.getLastPositionUpdateTime();
+        }
+
+        @DoNotInline
+        static long getActiveQueueItemId(PlaybackState state) {
+            return state.getActiveQueueItemId();
+        }
+
+        @DoNotInline
+        static PlaybackState.CustomAction.Builder createCustomActionBuilder(String action,
+                CharSequence name, int icon) {
+            return new PlaybackState.CustomAction.Builder(action, name, icon);
+        }
+
+        @DoNotInline
+        static void setExtras(PlaybackState.CustomAction.Builder builder, Bundle extras) {
+            builder.setExtras(extras);
+        }
+
+        @DoNotInline
+        static PlaybackState.CustomAction build(PlaybackState.CustomAction.Builder builder) {
+            return builder.build();
+        }
+
+        @DoNotInline
+        static Bundle getExtras(PlaybackState.CustomAction customAction) {
+            return customAction.getExtras();
+        }
+
+        @DoNotInline
+        static String getAction(PlaybackState.CustomAction customAction) {
+            return customAction.getAction();
+        }
+
+        @DoNotInline
+        static CharSequence getName(PlaybackState.CustomAction customAction) {
+            return customAction.getName();
+        }
+
+        @DoNotInline
+        static int getIcon(PlaybackState.CustomAction customAction) {
+            return customAction.getIcon();
+        }
+    }
+
+    @RequiresApi(22)
+    private static class Api22Impl {
+        private Api22Impl() {}
+
+        @DoNotInline
+        static void setExtras(PlaybackState.Builder builder, Bundle extras) {
+            builder.setExtras(extras);
+        }
+
+        @DoNotInline
+        static Bundle getExtras(PlaybackState state) {
+            return state.getExtras();
+        }
+    }
+}
\ No newline at end of file
diff --git a/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java b/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
index c923287..001a470 100644
--- a/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
+++ b/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
@@ -126,6 +126,7 @@
         return "AudioAttributesCompat: audioattributes=" + mAudioAttributes;
     }
 
+    @RequiresApi(21)
     static class Builder implements AudioAttributesImpl.Builder {
         final AudioAttributes.Builder mFwkBuilder;
 
diff --git a/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java b/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
index 144c340..031e2d1 100644
--- a/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
+++ b/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
@@ -50,6 +50,7 @@
         return mAudioAttributes.getVolumeControlStream();
     }
 
+    @RequiresApi(26)
     static class Builder extends AudioAttributesImplApi21.Builder {
         Builder() {
             super();
diff --git a/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java b/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
index a774664..d79fdc5 100644
--- a/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
+++ b/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
@@ -28,6 +28,7 @@
 import android.os.Looper;
 import android.os.Message;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -80,13 +81,8 @@
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            mFrameworkAudioFocusRequest =
-                    new AudioFocusRequest.Builder(mFocusGain)
-                            .setAudioAttributes(getAudioAttributes())
-                            .setWillPauseWhenDucked(mPauseOnDuck)
-                            .setOnAudioFocusChangeListener(
-                                    mOnAudioFocusChangeListener, mFocusChangeHandler)
-                            .build();
+            mFrameworkAudioFocusRequest = Api26Impl.createInstance(mFocusGain, getAudioAttributes(),
+                    mPauseOnDuck, mOnAudioFocusChangeListener, mFocusChangeHandler);
         } else {
             mFrameworkAudioFocusRequest = null;
         }
@@ -433,4 +429,23 @@
             return false;
         }
     }
+
+    @RequiresApi(26)
+    private static class Api26Impl {
+        private Api26Impl() {}
+
+        @DoNotInline
+        static AudioFocusRequest createInstance(
+                int focusGain,
+                AudioAttributes audioAttributes,
+                boolean pauseOnDuck,
+                OnAudioFocusChangeListener onAudioFocusChangeListener,
+                Handler focusChangeHandler) {
+            return new AudioFocusRequest.Builder(focusGain)
+                    .setAudioAttributes(audioAttributes)
+                    .setWillPauseWhenDucked(pauseOnDuck)
+                    .setOnAudioFocusChangeListener(onAudioFocusChangeListener, focusChangeHandler)
+                    .build();
+        }
+    }
 }
diff --git a/media/media/src/main/java/androidx/media/AudioManagerCompat.java b/media/media/src/main/java/androidx/media/AudioManagerCompat.java
index a6d4ae7..8215406 100644
--- a/media/media/src/main/java/androidx/media/AudioManagerCompat.java
+++ b/media/media/src/main/java/androidx/media/AudioManagerCompat.java
@@ -16,11 +16,14 @@
 
 package androidx.media;
 
+import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 import androidx.core.app.NotificationCompat.StreamType;
 
 /** Compatibility library for {@link AudioManager} with fallbacks for older platforms. */
@@ -88,7 +91,7 @@
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            return audioManager.requestAudioFocus(focusRequest.getAudioFocusRequest());
+            return Api26Impl.requestAudioFocus(audioManager, focusRequest.getAudioFocusRequest());
         } else {
             return audioManager.requestAudioFocus(
                     focusRequest.getOnAudioFocusChangeListener(),
@@ -117,7 +120,8 @@
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            return audioManager.abandonAudioFocusRequest(focusRequest.getAudioFocusRequest());
+            return Api26Impl.abandonAudioFocusRequest(audioManager,
+                    focusRequest.getAudioFocusRequest());
         } else {
             return audioManager.abandonAudioFocus(focusRequest.getOnAudioFocusChangeListener());
         }
@@ -145,11 +149,37 @@
     public static int getStreamMinVolume(@NonNull AudioManager audioManager,
             @StreamType int streamType) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            return audioManager.getStreamMinVolume(streamType);
+            return Api28Impl.getStreamMinVolume(audioManager, streamType);
         } else {
             return 0;
         }
     }
 
     private AudioManagerCompat() {}
+
+    @RequiresApi(26)
+    private static class Api26Impl {
+        private Api26Impl() {}
+
+        @DoNotInline
+        static int abandonAudioFocusRequest(AudioManager audioManager,
+                AudioFocusRequest focusRequest) {
+            return audioManager.abandonAudioFocusRequest(focusRequest);
+        }
+
+        @DoNotInline
+        static int requestAudioFocus(AudioManager audioManager, AudioFocusRequest focusRequest) {
+            return audioManager.requestAudioFocus(focusRequest);
+        }
+    }
+
+    @RequiresApi(28)
+    private static class Api28Impl {
+        private Api28Impl() {}
+
+        @DoNotInline
+        static int getStreamMinVolume(AudioManager audioManager, int streamType) {
+            return audioManager.getStreamMinVolume(streamType);
+        }
+    }
 }
diff --git a/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java b/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
index 05bbf1f..ffb99dc 100644
--- a/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
+++ b/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
@@ -322,21 +322,25 @@
             mHandler.postOrRun(new Runnable() {
                 @Override
                 public void run() {
-                    if (!mRootExtrasList.isEmpty()) {
-                        IMediaSession extraBinder = token.getExtraBinder();
-                        if (extraBinder != null) {
-                            for (Bundle rootExtras : mRootExtrasList) {
-                                BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
-                                        extraBinder.asBinder());
-                            }
-                        }
-                        mRootExtrasList.clear();
-                    }
-                    mServiceFwk.setSessionToken((MediaSession.Token) token.getToken());
+                    setSessionTokenOnHandler(token);
                 }
             });
         }
 
+        void setSessionTokenOnHandler(MediaSessionCompat.Token token) {
+            if (!mRootExtrasList.isEmpty()) {
+                IMediaSession extraBinder = token.getExtraBinder();
+                if (extraBinder != null) {
+                    for (Bundle rootExtras : mRootExtrasList) {
+                        BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
+                                extraBinder.asBinder());
+                    }
+                }
+                mRootExtrasList.clear();
+            }
+            mServiceFwk.setSessionToken((MediaSession.Token) token.getToken());
+        }
+
         @Override
         public void notifyChildrenChanged(final String parentId, final Bundle options) {
             notifyChildrenChangedForFramework(parentId, options);
@@ -490,6 +494,7 @@
             return mCurConnection.browserInfo;
         }
 
+        @RequiresApi(21)
         class MediaBrowserServiceApi21 extends MediaBrowserService {
             MediaBrowserServiceApi21(Context context) {
                 attachBaseContext(context);
diff --git a/media/media/src/main/java/androidx/media/VolumeProviderCompat.java b/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
index 201d74e..79e87f0 100644
--- a/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
+++ b/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
@@ -22,8 +22,10 @@
 import android.os.Build;
 import android.support.v4.media.session.MediaSessionCompat;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
 import java.lang.annotation.Retention;
@@ -143,7 +145,7 @@
         mCurrentVolume = currentVolume;
         if (Build.VERSION.SDK_INT >= 21) {
             VolumeProvider volumeProviderFwk = (VolumeProvider) getVolumeProvider();
-            volumeProviderFwk.setCurrentVolume(currentVolume);
+            Api21Impl.setCurrentVolume(volumeProviderFwk, currentVolume);
         }
         if (mCallback != null) {
             mCallback.onVolumeChanged(this);
@@ -235,4 +237,14 @@
     public static abstract class Callback {
         public abstract void onVolumeChanged(VolumeProviderCompat volumeProvider);
     }
+
+    @RequiresApi(21)
+    private static class Api21Impl {
+        private Api21Impl() {}
+
+        @DoNotInline
+        static void setCurrentVolume(VolumeProvider volumeProvider, int currentVolume) {
+            volumeProvider.setCurrentVolume(currentVolume);
+        }
+    }
 }
diff --git a/media/media/src/main/java/androidx/media/app/NotificationCompat.java b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
index e1fe44d..bc6510e 100644
--- a/media/media/src/main/java/androidx/media/app/NotificationCompat.java
+++ b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
@@ -30,6 +30,7 @@
 import android.view.View;
 import android.widget.RemoteViews;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.app.BundleCompat;
@@ -208,8 +209,8 @@
         @Override
         public void apply(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 21) {
-                builder.getBuilder().setStyle(
-                        fillInMediaStyle(new Notification.MediaStyle()));
+                Api21Impl.setStyle(builder.getBuilder(),
+                        fillInMediaStyle(Api21Impl.createMediaStyle()));
             } else if (mShowCancelButton) {
                 builder.getBuilder().setOngoing(true);
             }
@@ -218,10 +219,10 @@
         @RequiresApi(21)
         Notification.MediaStyle fillInMediaStyle(Notification.MediaStyle style) {
             if (mActionsToShowInCompact != null) {
-                style.setShowActionsInCompactView(mActionsToShowInCompact);
+                Api21Impl.setShowActionsInCompactView(style, mActionsToShowInCompact);
             }
             if (mToken != null) {
-                style.setMediaSession((MediaSession.Token) mToken.getToken());
+                Api21Impl.setMediaSession(style, (MediaSession.Token) mToken.getToken());
             }
             return style;
         }
@@ -285,7 +286,7 @@
                 button.setOnClickPendingIntent(R.id.action0, action.getActionIntent());
             }
             if (Build.VERSION.SDK_INT >= 15) {
-                button.setContentDescription(R.id.action0, action.getTitle());
+                Api15Impl.setContentDescription(button, R.id.action0, action.getTitle());
             }
             return button;
         }
@@ -386,8 +387,8 @@
         @Override
         public void apply(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 24) {
-                builder.getBuilder().setStyle(
-                        fillInMediaStyle(new Notification.DecoratedMediaCustomViewStyle()));
+                Api21Impl.setStyle(builder.getBuilder(),
+                        fillInMediaStyle(Api24Impl.createDecoratedMediaCustomViewStyle()));
             } else {
                 super.apply(builder);
             }
@@ -500,4 +501,50 @@
             views.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", color);
         }
     }
+
+    @RequiresApi(15)
+    private static class Api15Impl {
+        private Api15Impl() {}
+
+        @DoNotInline
+        static void setContentDescription(RemoteViews remoteViews, int viewId,
+                CharSequence contentDescription) {
+            remoteViews.setContentDescription(viewId, contentDescription);
+        }
+    }
+
+    @RequiresApi(21)
+    private static class Api21Impl {
+        private Api21Impl() {}
+
+        @DoNotInline
+        static void setStyle(Notification.Builder builder, Notification.Style style) {
+            builder.setStyle(style);
+        }
+
+        @DoNotInline
+        static Notification.MediaStyle createMediaStyle() {
+            return new Notification.MediaStyle();
+        }
+
+        @DoNotInline
+        static void setShowActionsInCompactView(Notification.MediaStyle style, int... actions) {
+            style.setShowActionsInCompactView(actions);
+        }
+
+        @DoNotInline
+        static void setMediaSession(Notification.MediaStyle style, MediaSession.Token token) {
+            style.setMediaSession(token);
+        }
+    }
+
+    @RequiresApi(24)
+    private static class Api24Impl {
+        private Api24Impl() {}
+
+        @DoNotInline
+        static Notification.DecoratedMediaCustomViewStyle createDecoratedMediaCustomViewStyle() {
+            return new Notification.DecoratedMediaCustomViewStyle();
+        }
+    }
 }
diff --git a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
index b6c2a3b..0d8dea6 100644
--- a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
+++ b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
@@ -39,6 +39,7 @@
 import android.view.KeyEvent;
 
 import androidx.annotation.RestrictTo;
+import androidx.core.content.ContextCompat;
 import androidx.media.MediaBrowserServiceCompat;
 
 import java.util.List;
@@ -114,7 +115,7 @@
                 getServiceComponentByAction(context, Intent.ACTION_MEDIA_BUTTON);
         if (mediaButtonServiceComponentName != null) {
             intent.setComponent(mediaButtonServiceComponentName);
-            startForegroundService(context, intent);
+            ContextCompat.startForegroundService(context, intent);
             return;
         }
         ComponentName mediaBrowserServiceComponentName = getServiceComponentByAction(context,
@@ -296,14 +297,6 @@
         return null;
     }
 
-    private static void startForegroundService(Context context, Intent intent) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            context.startForegroundService(intent);
-        } else {
-            context.startService(intent);
-        }
-    }
-
     private static ComponentName getServiceComponentByAction(Context context, String action) {
         PackageManager pm = context.getPackageManager();
         Intent queryIntent = new Intent(action);
diff --git a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
index 363f158..966e42c 100644
--- a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
+++ b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
@@ -21,7 +21,8 @@
     <TextView android:id="@+id/mr_chooser_title"
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
-              android:minHeight="64dp"
+              android:minHeight="52dp"
+              android:paddingTop="12dp"
               android:paddingLeft="24dp"
               android:paddingRight="24dp"
               android:gravity="center_vertical"
diff --git a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml
index 7fce7d5..e92def0 100644
--- a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml
+++ b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml
@@ -20,7 +20,8 @@
               android:minHeight="32dp"
               android:paddingLeft="24dp"
               android:paddingRight="24dp"
-              android:paddingBottom="24dp"
+              android:paddingTop="12dp"
+              android:paddingBottom="12dp"
               android:orientation="horizontal"
               android:gravity="center_vertical" >
 
diff --git a/navigation/benchmark/build.gradle b/navigation/benchmark/build.gradle
index 1bf4bf51..deaa318 100644
--- a/navigation/benchmark/build.gradle
+++ b/navigation/benchmark/build.gradle
@@ -13,9 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import static androidx.build.dependencies.DependenciesKt.*
+
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -29,12 +28,12 @@
     androidTestImplementation(projectOrArtifact(":benchmark:benchmark-junit4"))
     androidTestImplementation(project(":navigation:navigation-runtime"))
     androidTestImplementation(project(":internal-testutils-navigation"))
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.kotlinStdlib)
 }
 
 androidx {
diff --git a/navigation/integration-tests/safeargs-testapp/build.gradle b/navigation/integration-tests/safeargs-testapp/build.gradle
index ac8cc33..d522f1c 100644
--- a/navigation/integration-tests/safeargs-testapp/build.gradle
+++ b/navigation/integration-tests/safeargs-testapp/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 
@@ -66,6 +65,6 @@
 
 dependencies {
     implementation "${LibraryGroups.NAVIGATION}:navigation-runtime:${LibraryVersions.NAVIGATION}"
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
 }
diff --git a/navigation/integration-tests/testapp/build.gradle b/navigation/integration-tests/testapp/build.gradle
index 190bedf..4703da6e 100644
--- a/navigation/integration-tests/testapp/build.gradle
+++ b/navigation/integration-tests/testapp/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 buildscript {
     // TODO: Remove this when this test app no longer depends on 1.0.0 of vectordrawable-animated.
     // vectordrawable and vectordrawable-animated were accidentally using the same package name
@@ -30,7 +28,7 @@
 }
 
 dependencies {
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation("androidx.appcompat:appcompat:1.1.0")
     implementation(project(":navigation:navigation-fragment-ktx"))
     implementation(project(":navigation:navigation-ui-ktx"))
diff --git a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/HelpActivity.kt b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/HelpActivity.kt
index 55f03b2..5eb47d1 100644
--- a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/HelpActivity.kt
+++ b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/HelpActivity.kt
@@ -78,6 +78,7 @@
 }
 
 class BottomSheetNavigationView : BottomSheetDialogFragment() {
+    @Suppress("DEPRECATION")
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
diff --git a/navigation/navigation-common-ktx/build.gradle b/navigation/navigation-common-ktx/build.gradle
index 2e6b8bb..e7791a0 100644
--- a/navigation/navigation-common-ktx/build.gradle
+++ b/navigation/navigation-common-ktx/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
diff --git a/navigation/navigation-common/api/current.txt b/navigation/navigation-common/api/current.txt
index dda39ab..a889a60 100644
--- a/navigation/navigation-common/api/current.txt
+++ b/navigation/navigation-common/api/current.txt
@@ -226,7 +226,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
-    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
     ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
     method public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
     method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
@@ -281,7 +281,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
-    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
     ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
     method public final void addDestination(androidx.navigation.NavDestination destination);
     method public androidx.navigation.NavGraph build();
@@ -292,9 +292,9 @@
   }
 
   public final class NavGraphBuilderKt {
-    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
-    method public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
diff --git a/navigation/navigation-common/api/public_plus_experimental_current.txt b/navigation/navigation-common/api/public_plus_experimental_current.txt
index bd49a55..3dc13449 100644
--- a/navigation/navigation-common/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-common/api/public_plus_experimental_current.txt
@@ -263,7 +263,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
-    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
     ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
     method public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
     method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
@@ -322,7 +322,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
-    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
     ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
     method public final void addDestination(androidx.navigation.NavDestination destination);
     method public androidx.navigation.NavGraph build();
@@ -333,9 +333,9 @@
   }
 
   public final class NavGraphBuilderKt {
-    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
-    method public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
diff --git a/navigation/navigation-common/api/restricted_current.txt b/navigation/navigation-common/api/restricted_current.txt
index dda39ab..a889a60 100644
--- a/navigation/navigation-common/api/restricted_current.txt
+++ b/navigation/navigation-common/api/restricted_current.txt
@@ -226,7 +226,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
-    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
     ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
     method public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
     method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
@@ -281,7 +281,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
-    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
     ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
     method public final void addDestination(androidx.navigation.NavDestination destination);
     method public androidx.navigation.NavGraph build();
@@ -292,9 +292,9 @@
   }
 
   public final class NavGraphBuilderKt {
-    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
-    method public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index fe1af8a..0a541e1 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -35,22 +32,22 @@
     implementation("androidx.core:core-ktx:1.1.0")
     implementation("androidx.collection:collection-ktx:1.1.0")
 
-    api(KOTLIN_STDLIB)
+    api(libs.kotlinStdlib)
     testImplementation(project(":navigation:navigation-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
-    testImplementation(TRUTH)
-    testImplementation(KOTLIN_STDLIB)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
+    testImplementation(libs.truth)
+    testImplementation(libs.kotlinStdlib)
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
-    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
+    androidTestImplementation(libs.kotlinStdlib)
 }
 
 //used by testImplementation safe-args-generator
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDestinationBuilderTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDestinationBuilderTest.kt
index 68fa76a..6572323 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDestinationBuilderTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDestinationBuilderTest.kt
@@ -124,6 +124,7 @@
  * a NavDestination directly to allow for testing NavDestinationBuilder in
  * isolation.
  */
+@Suppress("DEPRECATION")
 fun NavigatorProvider.navDestination(
     @IdRes id: Int,
     builder: NavDestinationBuilder<NavDestination>.() -> Unit
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavGraphBuilderTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavGraphBuilderTest.kt
index cb75a08..69d1eda 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavGraphBuilderTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavGraphBuilderTest.kt
@@ -32,6 +32,7 @@
         addNavigator(NoOpNavigator())
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun navigation() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
@@ -54,6 +55,7 @@
             .isTrue()
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun navigationUnaryPlus() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
@@ -80,6 +82,7 @@
             .isTrue()
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun navigationAddDestination() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
@@ -108,6 +111,7 @@
             .isTrue()
     }
 
+    @Suppress("DEPRECATION")
     @Test(expected = IllegalStateException::class)
     fun navigationMissingStartDestination() {
         provider.navigation(startDestination = 0) {
@@ -124,6 +128,7 @@
         fail("NavGraph should throw IllegalStateException if no startDestinationRoute is set")
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun navigationNested() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
@@ -158,6 +163,7 @@
  * Create a base NavDestination. Generally, only subtypes of NavDestination should be
  * added to a NavGraph (hence why this is not in the common-ktx library)
  */
+@Suppress("DEPRECATION")
 fun NavGraphBuilder.navDestination(
     @IdRes id: Int,
     builder: NavDestinationBuilder<NavDestination>.() -> Unit
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavDestinationBuilder.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavDestinationBuilder.kt
index f5453fd..22c9fa3 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavDestinationBuilder.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavDestinationBuilder.kt
@@ -51,6 +51,10 @@
      *
      * @return the newly constructed [NavDestination]
      */
+    @Deprecated(
+        "Use routes to build your NavDestination instead",
+        ReplaceWith("NavDestinationBuilder(navigator, route = id.toString())")
+    )
     public constructor(navigator: Navigator<out D>, @IdRes id: Int) :
         this(navigator, id, null)
 
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphBuilder.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphBuilder.kt
index 28a4c0a..d69f5d2 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphBuilder.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphBuilder.kt
@@ -27,6 +27,14 @@
  *
  * @return the newly constructed NavGraph
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to build your NavGraph instead",
+    ReplaceWith(
+        "navigation(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun NavigatorProvider.navigation(
     @IdRes id: Int = 0,
     @IdRes startDestination: Int,
@@ -58,6 +66,14 @@
  *
  * @return the newly constructed nested NavGraph
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to build your nested NavGraph instead",
+    ReplaceWith(
+        "navigation(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun NavGraphBuilder.navigation(
     @IdRes id: Int,
     @IdRes startDestination: Int,
@@ -100,6 +116,14 @@
      *
      * @return the newly created NavGraph
      */
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to build your NavGraph instead",
+        ReplaceWith(
+            "NavGraphBuilder(provider, startDestination = startDestination.toString(), " +
+                "route = id.toString())"
+        )
+    )
     public constructor(
         provider: NavigatorProvider,
         @IdRes id: Int,
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index 11e4895..7966a17 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -15,12 +15,9 @@
  */
 
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 import androidx.build.RunApiTasks
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -31,7 +28,7 @@
 dependencies {
     kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
 
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
     implementation(projectOrArtifact(":compose:foundation:foundation-layout"))
     api(projectOrArtifact(":activity:activity-compose"))
     api(projectOrArtifact(":compose:runtime:runtime"))
@@ -47,9 +44,9 @@
         exclude group: "androidx.navigation", module: "navigation-common"
     })
     androidTestImplementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
 }
 
 androidx {
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle b/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
index cf1461d..fb50bcb 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
@@ -14,11 +14,8 @@
  * limitations under the License.
  */
 
-
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.getKOTLIN_STDLIB
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -28,7 +25,7 @@
 
 dependencies {
     kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
 
     implementation(projectOrArtifact(":compose:integration-tests:demos:common"))
     implementation(projectOrArtifact(":compose:foundation:foundation"))
diff --git a/navigation/navigation-compose/samples/build.gradle b/navigation/navigation-compose/samples/build.gradle
index 778a74f..7dc352f 100644
--- a/navigation/navigation-compose/samples/build.gradle
+++ b/navigation/navigation-compose/samples/build.gradle
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.LibraryType
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -30,7 +26,7 @@
 
 dependencies {
     kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.kotlinStdlib)
 
     compileOnly(projectOrArtifact(":annotation:annotation-sampled"))
     implementation(projectOrArtifact(":compose:foundation:foundation"))
diff --git a/navigation/navigation-dynamic-features-fragment/api/current.txt b/navigation/navigation-dynamic-features-fragment/api/current.txt
index e6dd027..d42b4d5 100644
--- a/navigation/navigation-dynamic-features-fragment/api/current.txt
+++ b/navigation/navigation-dynamic-features-fragment/api/current.txt
@@ -15,7 +15,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
-    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
     ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
     method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
     method public String? getModuleName();
@@ -24,7 +24,7 @@
   }
 
   public final class DynamicFragmentNavigatorDestinationBuilderKt {
-    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
     method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
diff --git a/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt
index e6dd027..d42b4d5 100644
--- a/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_current.txt
@@ -15,7 +15,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
-    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
     ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
     method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
     method public String? getModuleName();
@@ -24,7 +24,7 @@
   }
 
   public final class DynamicFragmentNavigatorDestinationBuilderKt {
-    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
     method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
diff --git a/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt b/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt
index e6dd027..d42b4d5 100644
--- a/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt
+++ b/navigation/navigation-dynamic-features-fragment/api/restricted_current.txt
@@ -15,7 +15,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
-    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
     ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
     method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
     method public String? getModuleName();
@@ -24,7 +24,7 @@
   }
 
   public final class DynamicFragmentNavigatorDestinationBuilderKt {
-    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
     method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
diff --git a/navigation/navigation-dynamic-features-fragment/build.gradle b/navigation/navigation-dynamic-features-fragment/build.gradle
index 708a89b..5448fa7 100644
--- a/navigation/navigation-dynamic-features-fragment/build.gradle
+++ b/navigation/navigation-dynamic-features-fragment/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -35,28 +32,28 @@
 dependencies {
     api(project(":navigation:navigation-dynamic-features-runtime"))
     api(project(":navigation:navigation-fragment"))
-    api(KOTLIN_STDLIB)
+    api(libs.kotlinStdlib)
 
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
-    testImplementation(TRUTH)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testExtJunit)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.truth)
 
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MULTIDEX)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.multidex)
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.fragment", module: "fragment"
     })
-    androidTestImplementation(MULTIDEX)
 }
 
 android {
diff --git a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
index 30da2f9..6f6d8d7 100644
--- a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
@@ -33,13 +33,14 @@
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 public class DynamicFragmentNavigatorDestinationBuilderTest {
-    @Suppress("DEPRECATION")
+
     @get:Rule
     public val rule: ActivityScenarioRule<TestActivity> = ActivityScenarioRule(
         TestActivity::class.java
     )
     private val fragmentManager get() = rule.withActivity { supportFragmentManager }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     public fun reified() {
@@ -56,6 +57,7 @@
             .isEqualTo(TestFragment::class.java.name)
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     public fun moduleName() {
@@ -77,6 +79,7 @@
             .isEqualTo(MODULE_NAME)
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     public fun no_moduleName() {
diff --git a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
index 3d0cde7..c1a8f7d 100644
--- a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
@@ -35,9 +35,8 @@
 @RunWith(AndroidJUnit4::class)
 public class DynamicNavHostFragmentTest {
 
-    @Suppress("DEPRECATION")
     @get:Rule
-    public val activityTestRule: ActivityScenarioRule<NavigationActivity> = ActivityScenarioRule(
+    public val rule: ActivityScenarioRule<NavigationActivity> = ActivityScenarioRule(
         NavigationActivity::class.java
     )
 
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilder.kt b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilder.kt
index dd0b7f2..d337aa2 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilder.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/main/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilder.kt
@@ -29,6 +29,11 @@
  * Construct a new [DynamicFragmentNavigator.Destination]
  * @param id Destination id.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your DynamicFragmentDestination instead",
+    ReplaceWith("fragment(route = id.toString())")
+)
 public inline fun <reified F : Fragment> DynamicNavGraphBuilder.fragment(
     @IdRes id: Int
 ): Unit = fragment<F>(id) {}
@@ -37,6 +42,11 @@
  * Construct a new [DynamicFragmentNavigator.Destination]
  * @param id Destination id.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your DynamicFragmentDestination instead",
+    ReplaceWith("fragment(route = id.toString()) { builder.invoke() }")
+)
 public inline fun <reified F : Fragment> DynamicNavGraphBuilder.fragment(
     @IdRes id: Int,
     builder: DynamicFragmentNavigatorDestinationBuilder.() -> Unit
@@ -47,6 +57,11 @@
  * @param id Destination id.
  * @param fragmentClassName Fully qualified class name of destination Fragment.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your DynamicFragmentDestination instead",
+    ReplaceWith("fragment(route = id.toString(), fragmentClassName) { builder.invoke() }")
+)
 public inline fun DynamicNavGraphBuilder.fragment(
     @IdRes id: Int,
     fragmentClassName: String,
@@ -102,6 +117,14 @@
 
     private var fragmentClassName: String
 
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to create your DynamicFragmentDestinationBuilder instead",
+        ReplaceWith(
+            "DynamicFragmentNavigatorDestinationBuilder(navigator, route = id.toString(), " +
+                "fragmentClassName)"
+        )
+    )
     public constructor(
         navigator: DynamicFragmentNavigator,
         @IdRes id: Int,
diff --git a/navigation/navigation-dynamic-features-runtime/api/current.txt b/navigation/navigation-dynamic-features-runtime/api/current.txt
index 9807b58..ea49279 100644
--- a/navigation/navigation-dynamic-features-runtime/api/current.txt
+++ b/navigation/navigation-dynamic-features-runtime/api/current.txt
@@ -15,7 +15,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
-    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
     ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
     method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
     method public String? getAction();
@@ -39,7 +39,7 @@
   }
 
   public final class DynamicActivityNavigatorDestinationBuilderKt {
-    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
   }
 
@@ -87,7 +87,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
-    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
     ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
     method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
     method public String? getGraphPackage();
@@ -96,8 +96,8 @@
   }
 
   public final class DynamicIncludeNavGraphBuilderKt {
-    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
-    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
     method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
     method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
   }
@@ -120,7 +120,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
-    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
     ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
     method public String? getModuleName();
     method public int getProgressDestination();
@@ -134,19 +134,20 @@
   }
 
   public final class DynamicNavGraphBuilderKt {
-    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
-    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavControllerKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavHostKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
 }
diff --git a/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_current.txt b/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_current.txt
index 9807b58..ea49279 100644
--- a/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_current.txt
@@ -15,7 +15,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
-    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
     ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
     method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
     method public String? getAction();
@@ -39,7 +39,7 @@
   }
 
   public final class DynamicActivityNavigatorDestinationBuilderKt {
-    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
   }
 
@@ -87,7 +87,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
-    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
     ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
     method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
     method public String? getGraphPackage();
@@ -96,8 +96,8 @@
   }
 
   public final class DynamicIncludeNavGraphBuilderKt {
-    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
-    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
     method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
     method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
   }
@@ -120,7 +120,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
-    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
     ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
     method public String? getModuleName();
     method public int getProgressDestination();
@@ -134,19 +134,20 @@
   }
 
   public final class DynamicNavGraphBuilderKt {
-    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
-    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavControllerKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavHostKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
 }
diff --git a/navigation/navigation-dynamic-features-runtime/api/restricted_current.txt b/navigation/navigation-dynamic-features-runtime/api/restricted_current.txt
index 9807b58..ea49279 100644
--- a/navigation/navigation-dynamic-features-runtime/api/restricted_current.txt
+++ b/navigation/navigation-dynamic-features-runtime/api/restricted_current.txt
@@ -15,7 +15,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
-    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
     ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
     method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
     method public String? getAction();
@@ -39,7 +39,7 @@
   }
 
   public final class DynamicActivityNavigatorDestinationBuilderKt {
-    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
   }
 
@@ -87,7 +87,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
-    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
     ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
     method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
     method public String? getGraphPackage();
@@ -96,8 +96,8 @@
   }
 
   public final class DynamicIncludeNavGraphBuilderKt {
-    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
-    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
     method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
     method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
   }
@@ -120,7 +120,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
-    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
     ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
     method public String? getModuleName();
     method public int getProgressDestination();
@@ -134,19 +134,20 @@
   }
 
   public final class DynamicNavGraphBuilderKt {
-    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
-    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavControllerKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
   public final class NavHostKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
   }
 
 }
diff --git a/navigation/navigation-dynamic-features-runtime/build.gradle b/navigation/navigation-dynamic-features-runtime/build.gradle
index 09812400..e1fc340 100644
--- a/navigation/navigation-dynamic-features-runtime/build.gradle
+++ b/navigation/navigation-dynamic-features-runtime/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -34,28 +31,30 @@
 
 dependencies {
     api(project(":navigation:navigation-runtime"))
-    api(PLAY_CORE)
-    api(KOTLIN_STDLIB)
+    api(libs.playCore)
 
     testImplementation(project(":navigation:navigation-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
-    testImplementation(ANDROIDX_TEST_CORE)
-    testImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    testImplementation(ANDROIDX_TEST_RUNNER)
-    testImplementation(JUNIT)
-    testImplementation(MOCKITO_CORE)
-    testImplementation(ROBOLECTRIC)
-    testImplementation(TRUTH)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testExtJunit)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.truth)
 
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MULTIDEX)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.multidex)
+    androidTestImplementation(project(":internal-testutils-runtime"), {
+        exclude group: "androidx.fragment", module: "fragment"
+    })
 }
 
 androidx {
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt
index 5fd5b65..f8eb54b 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt
@@ -29,7 +29,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class DynamicActivityNavigatorDestinationBuilderTest {
+public class DynamicActivityNavigatorDestinationBuilderTest {
 
     private val context: Context = ApplicationProvider.getApplicationContext()
 
@@ -46,8 +46,9 @@
             )
         }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun module() {
+    public fun module() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
             activity(DESTINATION_ID) {
                 moduleName = MODULE_NAME
@@ -60,8 +61,9 @@
             .isEqualTo(MODULE_NAME)
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun noModule() {
+    public fun noModule() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
             activity(DESTINATION_ID) {
             }
@@ -71,8 +73,9 @@
         ).isNull()
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun activity() {
+    public fun activity() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
             activity(DESTINATION_ID) {
                 moduleName = MODULE_NAME
@@ -90,8 +93,9 @@
         )
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun noActivity() {
+    public fun noActivity() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
             activity(DESTINATION_ID) {
             }
@@ -102,8 +106,9 @@
         ).isNull()
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun modulePackage() {
+    public fun modulePackage() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
             activity(DESTINATION_ID) {
                 moduleName = MODULE_NAME
@@ -116,7 +121,7 @@
     }
 
     @Test
-    fun moduleRoute() {
+    public fun moduleRoute() {
         val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
             activity(DESTINATION_ROUTE) {
                 moduleName = MODULE_NAME
@@ -130,7 +135,7 @@
     }
 
     @Test
-    fun noModuleRoute() {
+    public fun noModuleRoute() {
         val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
             activity(DESTINATION_ROUTE) {
             }
@@ -141,7 +146,7 @@
     }
 
     @Test
-    fun activityRoute() {
+    public fun activityRoute() {
         val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
             activity(DESTINATION_ROUTE) {
                 moduleName = MODULE_NAME
@@ -160,7 +165,7 @@
     }
 
     @Test
-    fun noActivityRoute() {
+    public fun noActivityRoute() {
         val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
             activity(DESTINATION_ROUTE) {
             }
@@ -172,7 +177,7 @@
     }
 
     @Test
-    fun modulePackageRoute() {
+    public fun modulePackageRoute() {
         val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
             activity(DESTINATION_ROUTE) {
                 moduleName = MODULE_NAME
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
index 06fcce8..c072251 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
@@ -20,8 +20,10 @@
 import android.content.Intent
 import androidx.navigation.NavigatorProvider
 import androidx.navigation.NoOpNavigator
+import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import androidx.testutils.withActivity
 import com.google.android.play.core.splitinstall.SplitInstallManager
 import org.junit.Assert.assertNotNull
 import org.junit.Before
@@ -31,11 +33,12 @@
 import org.mockito.Mockito.mock
 /* ktlint-disable no-unused-imports */ // https://github.com/pinterest/ktlint/issues/937
 import org.mockito.Mockito.`when` as mockWhen
+
 /* ktlint-enable unused-imports */
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class DynamicActivityNavigatorTest {
+public class DynamicActivityNavigatorTest {
 
     private lateinit var navigator: DynamicActivityNavigator
     private lateinit var installManager: DynamicInstallManager
@@ -46,44 +49,54 @@
 
     @Suppress("DEPRECATION")
     @get:Rule
-    val activityTestRule = androidx.test.rule.ActivityTestRule(NavigationActivity::class.java)
+    public val activityRule: ActivityScenarioRule<NavigationActivity> =
+        ActivityScenarioRule(NavigationActivity::class.java)
 
     @Before
-    fun setup() {
+    public fun setup() {
         splitInstallManager = mock(SplitInstallManager::class.java)
-        installManager = DynamicInstallManager(activityTestRule.activity, splitInstallManager)
-        navigator = DynamicActivityNavigator(activityTestRule.activity, installManager)
+        activityRule.withActivity {
+            installManager = DynamicInstallManager(
+                this,
+                splitInstallManager
+            )
+            navigator = DynamicActivityNavigator(this, installManager)
+            dynamicDestination = navigator.createDestination()
+            dynamicDestination.setIntent(
+                Intent(this, DestinationActivity::class.java)
+            )
+        }
         provider = NavigatorProvider()
         noOpNavigator = NoOpNavigator()
         provider.addNavigator(noOpNavigator)
-        dynamicDestination = navigator.createDestination()
-        dynamicDestination.setIntent(
-            Intent(activityTestRule.activity, DestinationActivity::class.java)
-        )
     }
 
     @Test
-    fun navigate_DynamicActivityDestination() {
+    public fun navigate_DynamicActivityDestination() {
         navigator.navigate(dynamicDestination, null, null, null)
     }
 
     @Test(expected = IllegalStateException::class)
-    fun navigate_DynamicActivityDestination_NoDynamicNavGraph() {
+    public fun navigate_DynamicActivityDestination_NoDynamicNavGraph() {
+        lateinit var activity: NavigationActivity
+        activityRule.scenario.onActivity {
+            activity = it
+        }
         @Suppress("UNUSED_VARIABLE")
         val destination = DynamicActivityNavigator.Destination(NavigatorProvider())
         val navDestination = mock(DynamicActivityNavigator.Destination::class.java).apply {
             mockWhen(moduleName).thenReturn("module")
-            setIntent(Intent(activityTestRule.activity, DestinationActivity::class.java))
+            setIntent(Intent(activity, DestinationActivity::class.java))
         }
         navigator.navigate(navDestination, null, null, null)
     }
 
     @Test
-    fun createDestination() {
+    public fun createDestination() {
         assertNotNull(navigator.createDestination())
     }
 }
 
-class NavigationActivity : Activity()
+public class NavigationActivity : Activity()
 
-class DestinationActivity : Activity()
+public class DestinationActivity : Activity()
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt
index ab2697a..9b562d7 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt
@@ -16,13 +16,16 @@
 
 package androidx.navigation.dynamicfeatures
 
+import android.content.Context
 import android.os.Bundle
 import androidx.navigation.NavController
 import androidx.navigation.NoOpNavigator
 import androidx.navigation.dynamicfeatures.shared.AndroidTestDynamicInstallManager
 import androidx.navigation.dynamicfeatures.test.R
+import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.fail
@@ -34,21 +37,26 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-class DynamicIncludeGraphNavigatorTest {
+public class DynamicIncludeGraphNavigatorTest {
 
     private lateinit var navigator: DynamicIncludeGraphNavigator
+    private lateinit var context: Context
 
-    @Suppress("DEPRECATION")
     @get:Rule
-    val rule = androidx.test.rule.ActivityTestRule(NavigationActivity::class.java)
+    public val rule: ActivityScenarioRule<NavigationActivity> =
+        ActivityScenarioRule(NavigationActivity::class.java)
 
     @Before
-    fun setup() {
+    public fun setup() {
         setupInternal()
     }
 
     private fun setupInternal(navGraphId: Int = R.navigation.nav_graph) {
-        val context = rule.activity
+
+        rule.withActivity {
+            context = this
+        }
+
         val navController = NavController(context)
         val navigatorProvider = navController.navigatorProvider
         val installManager = AndroidTestDynamicInstallManager(context).also {
@@ -72,13 +80,12 @@
     }
 
     @Test
-    fun createDestination() {
+    public fun createDestination() {
         assertNotNull(navigator.createDestination())
     }
 
     @Test
-    fun testReplacePackagePlaceholder() {
-        val context = rule.activity
+    public fun testReplacePackagePlaceholder() {
         val packageName = context.packageName
         val dynamicNavGraph = navigator.createDestination().apply {
             moduleName = FEATURE_NAME
@@ -105,7 +112,7 @@
     }
 
     @Test
-    fun invalidGraphId() {
+    public fun invalidGraphId() {
         try {
             setupInternal(R.navigation.nav_invalid_id)
             fail("Inflating nav_invalid_id should fail with an IllegalStateException")
@@ -120,23 +127,23 @@
     }
 
     @Test
-    fun onSaveState() {
+    public fun onSaveState() {
         assertThat(navigator.onSaveState()).isEqualTo(Bundle.EMPTY)
     }
 
     @Test
-    fun onRestoreState() {
+    public fun onRestoreState() {
         navigator.onRestoreState(Bundle.EMPTY)
     }
 
     @Test
-    fun onRestoreState_nestedInclusion() {
+    public fun onRestoreState_nestedInclusion() {
         setupInternal(R.navigation.nav_graph_nested_include_dynamic)
         navigator.onRestoreState(Bundle.EMPTY)
     }
 
     @Test
-    fun popBackStack() {
+    public fun popBackStack() {
         assertThat(navigator.popBackStack()).isTrue()
     }
 }
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt
index aa3b07d..d90be5f 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt
@@ -33,7 +33,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class DynamicIncludeNavGraphBuilderTest {
+public class DynamicIncludeNavGraphBuilderTest {
 
     private val context: Context = ApplicationProvider.getApplicationContext()
     private val navController = NavController(context).apply {
@@ -44,8 +44,9 @@
         navigatorProvider += NoOpNavigator()
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun includeDynamic() {
+    public fun includeDynamic() {
         val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
             includeDynamic(GRAPH_ID, MODULE_NAME, GRAPH_RESOURCE_NAME) {
                 graphPackage = GRAPH_PACKAGE
@@ -65,7 +66,9 @@
             .isEqualTo(GRAPH_RESOURCE_NAME)
     }
 
-    fun includeDynamic_emptyModuleName() {
+    @Suppress("DEPRECATION")
+    @Test
+    public fun includeDynamic_emptyModuleName() {
         navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
             try {
                 includeDynamic(GRAPH_ID, "", GRAPH_RESOURCE_NAME)
@@ -76,8 +79,9 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun includeDynamic_graphPackage_null() {
+    public fun includeDynamic_graphPackage_null() {
         val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
             includeDynamic(GRAPH_ID, MODULE_NAME, GRAPH_RESOURCE_NAME)
         }
@@ -87,8 +91,9 @@
             .that(includeDynamic.graphPackage).isEqualTo("${context.packageName}.$MODULE_NAME")
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun includeDynamic_graphPackage_empty() {
+    public fun includeDynamic_graphPackage_empty() {
         navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
             try {
                 includeDynamic(GRAPH_ID, MODULE_NAME, GRAPH_RESOURCE_NAME) {
@@ -101,8 +106,9 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun includeDynamic_graphResourceName_empty() {
+    public fun includeDynamic_graphResourceName_empty() {
         navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
             try {
                 includeDynamic(GRAPH_ID, MODULE_NAME, "")
@@ -114,7 +120,7 @@
     }
 
     @Test
-    fun includeDynamicRoute() {
+    public fun includeDynamicRoute() {
         val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
             includeDynamic(GRAPH_ROUTE, MODULE_NAME, GRAPH_RESOURCE_NAME) {
                 graphPackage = GRAPH_PACKAGE
@@ -135,7 +141,7 @@
             .isEqualTo(GRAPH_RESOURCE_NAME)
     }
 
-    fun includeDynamic_emptyModuleNameRoute() {
+    public fun includeDynamic_emptyModuleNameRoute() {
         navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
             try {
                 includeDynamic(GRAPH_ROUTE, "", GRAPH_RESOURCE_NAME)
@@ -147,7 +153,7 @@
     }
 
     @Test
-    fun includeDynamic_graphPackage_nullRoute() {
+    public fun includeDynamic_graphPackage_nullRoute() {
         val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
             includeDynamic(GRAPH_ROUTE, MODULE_NAME, GRAPH_RESOURCE_NAME)
         }
@@ -159,7 +165,7 @@
     }
 
     @Test
-    fun includeDynamic_graphPackage_emptyRoute() {
+    public fun includeDynamic_graphPackage_emptyRoute() {
         navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
             try {
                 includeDynamic(GRAPH_ROUTE, MODULE_NAME, GRAPH_RESOURCE_NAME) {
@@ -173,7 +179,7 @@
     }
 
     @Test
-    fun includeDynamic_graphResourceName_emptyRoute() {
+    public fun includeDynamic_graphResourceName_emptyRoute() {
         navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
             try {
                 includeDynamic(GRAPH_ROUTE, MODULE_NAME, "")
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt
index de954ca..cbbcf25 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt
@@ -32,7 +32,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class DynamicNavGraphBuilderTest {
+public class DynamicNavGraphBuilderTest {
 
     private val provider = NavigatorProvider().apply {
         addNavigator(
@@ -44,8 +44,9 @@
         addNavigator(NoOpNavigator())
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun navigation() {
+    public fun navigation() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
             moduleName = MODULE_NAME
             navDestination(DESTINATION_ID) {}
@@ -59,15 +60,17 @@
             .isEqualTo(MODULE_NAME)
     }
 
-    fun navigation_emptyModuleName() {
+    @Suppress("DEPRECATION")
+    public fun navigation_emptyModuleName() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
         }
         assertWithMessage("Without a moduleName the graph should be a NavGraph")
             .that(graph !is DynamicGraphNavigator.DynamicNavGraph)
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun progressDestination() {
+    public fun progressDestination() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
             moduleName = MODULE_NAME
             progressDestination = PROGRESS_DESTINATION_ID
@@ -83,8 +86,9 @@
             .isTrue()
     }
 
+    @Suppress("DEPRECATION")
     @Test
-    fun progressDestination_notSet() {
+    public fun progressDestination_notSet() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
             moduleName = MODULE_NAME
         } as DynamicGraphNavigator.DynamicNavGraph
@@ -95,7 +99,7 @@
     }
 
     @Test
-    fun navigationRoute() {
+    public fun navigationRoute() {
         val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
             moduleName = MODULE_NAME
             navDestination(DESTINATION_ROUTE) {}
@@ -109,7 +113,7 @@
             .isEqualTo(MODULE_NAME)
     }
 
-    fun navigation_emptyModuleNameRoute() {
+    public fun navigation_emptyModuleNameRoute() {
         val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
         }
         assertWithMessage("Without a moduleName the graph should be a NavGraph")
@@ -117,7 +121,7 @@
     }
 
     @Test
-    fun progressDestinationRoute() {
+    public fun progressDestinationRoute() {
         val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
             moduleName = MODULE_NAME
             progressDestinationRoute = PROGRESS_DESTINATION_ROUTE
@@ -134,7 +138,7 @@
     }
 
     @Test
-    fun progressDestination_notSetRoute() {
+    public fun progressDestination_notSetRoute() {
         val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
             moduleName = MODULE_NAME
         } as DynamicGraphNavigator.DynamicNavGraph
@@ -155,16 +159,17 @@
  * Create a base NavDestination. Generally, only subtypes of NavDestination should be
  * added to a NavGraph (hence why this is not in the common-ktx library)
  */
-fun DynamicNavGraphBuilder.navDestination(
+@Suppress("DEPRECATION")
+public fun DynamicNavGraphBuilder.navDestination(
     @IdRes id: Int,
     builder: NavDestinationBuilder<NavDestination>.() -> Unit
-) = destination(NavDestinationBuilder(provider[NoOpNavigator::class], id).apply(builder))
+): Unit = destination(NavDestinationBuilder(provider[NoOpNavigator::class], id).apply(builder))
 
 /**
  * Create a base NavDestination. Generally, only subtypes of NavDestination should be
  * added to a NavGraph (hence why this is not in the common-ktx library)
  */
-fun DynamicNavGraphBuilder.navDestination(
+public fun DynamicNavGraphBuilder.navDestination(
     route: String,
     builder: NavDestinationBuilder<NavDestination>.() -> Unit
-) = destination(NavDestinationBuilder(provider[NoOpNavigator::class], route).apply(builder))
+): Unit = destination(NavDestinationBuilder(provider[NoOpNavigator::class], route).apply(builder))
\ No newline at end of file
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt
index 60580b4..c400233 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt
@@ -24,7 +24,7 @@
 /**
  * A dynamic install manager used for testing.
  */
-class AndroidTestDynamicInstallManager(
+public class AndroidTestDynamicInstallManager(
     context: Context,
-    val splitInstallManager: SplitInstallManager = mock(SplitInstallManager::class.java)
+    public val splitInstallManager: SplitInstallManager = mock(SplitInstallManager::class.java)
 ) : DynamicInstallManager(context, splitInstallManager)
diff --git a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilder.kt b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilder.kt
index 67dde4b..7fefcef 100644
--- a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilder.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilder.kt
@@ -30,6 +30,11 @@
  * Construct a new [DynamicActivityNavigator.Destination]
  * @param id Destination id.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to build your DynamicActivityDestination instead",
+    ReplaceWith("activity(route = id.toString()) { builder.invoke() }")
+)
 public inline fun DynamicNavGraphBuilder.activity(
     @IdRes id: Int,
     builder: DynamicActivityNavigatorDestinationBuilder.() -> Unit
@@ -62,6 +67,13 @@
     NavDestinationBuilder<ActivityNavigator.Destination> {
     private var activityNavigator: DynamicActivityNavigator
 
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to build your DynamicActivityDestination instead",
+        ReplaceWith(
+            "DynamicActivityNavigatorDestinationBuilder(activityNavigator, route = id.toString())"
+        )
+    )
     public constructor(
         activityNavigator: DynamicActivityNavigator,
         @IdRes id: Int
diff --git a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilder.kt b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilder.kt
index 4b8cc85..a59d702 100644
--- a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilder.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilder.kt
@@ -33,6 +33,11 @@
  * @param graphResourceName Graph's resource name without the `navigation` qualifier. This
  * must not be an empty string.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to include your DynamicNavGraph instead",
+    ReplaceWith("includeDynamic(route = id.toString(), moduleName, graphResourceName)")
+)
 public inline fun DynamicNavGraphBuilder.includeDynamic(
     @IdRes id: Int,
     moduleName: String,
@@ -49,6 +54,13 @@
  * must not be an empty string.
  * @param builder Another builder for chaining.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to include your DynamicNavGraph instead",
+    ReplaceWith(
+        "includeDynamic(route = id.toString(), moduleName, graphResourceName) { builder.invoke() }"
+    )
+)
 public inline fun DynamicNavGraphBuilder.includeDynamic(
     @IdRes id: Int,
     moduleName: String,
@@ -112,6 +124,14 @@
     private var moduleName: String
     private var graphResourceName: String
 
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to create your DynamicIncludeNavGraphBuilder instead",
+        ReplaceWith(
+            "DynamicIncludeNavGraphBuilder(dynamicIncludeGraphNavigator, route = id.toString(), " +
+                "moduleName, graphResourceName)"
+        )
+    )
     public constructor(
         dynamicIncludeGraphNavigator: DynamicIncludeGraphNavigator,
         @IdRes id: Int,
diff --git a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilder.kt b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilder.kt
index 1919a1f..367d36f 100644
--- a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilder.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilder.kt
@@ -32,6 +32,14 @@
  * @param startDestination Id start destination in the graph
  * @param builder Another builder for chaining.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your DynamicNavGraph instead",
+    ReplaceWith(
+        "navigation(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun NavigatorProvider.navigation(
     @IdRes id: Int = 0,
     @IdRes startDestination: Int,
@@ -49,6 +57,14 @@
  * @param startDestination Id start destination in the graph
  * @param builder Another builder for chaining.
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your DynamicNavGraph instead",
+    ReplaceWith(
+        "navigation(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun DynamicNavGraphBuilder.navigation(
     @IdRes id: Int,
     @IdRes startDestination: Int,
@@ -105,6 +121,14 @@
     @IdRes private var startDestinationId: Int = 0
     private var startDestinationRoute: String? = null
 
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to create your DynamicNavGraphBuilder instead",
+        ReplaceWith(
+            "DynamicNavGraphBuilder(provider, startDestination = startDestination.toString(), " +
+                "route = id.toString())"
+        )
+    )
     public constructor(
         provider: NavigatorProvider,
         @IdRes id: Int,
diff --git a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavController.kt b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavController.kt
index f9e3d8f..f35e4e4 100644
--- a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavController.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavController.kt
@@ -23,6 +23,14 @@
 /**
  * Construct a new [androidx.navigation.NavGraph] that supports dynamic navigation destinations
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your dynamic NavGraph instead",
+    ReplaceWith(
+        "createGraph(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun NavController.createGraph(
     @IdRes id: Int = 0,
     @IdRes startDestination: Int,
diff --git a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavHost.kt b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavHost.kt
index 800645a..9bd3e8ee 100644
--- a/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavHost.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/main/java/androidx/navigation/dynamicfeatures/NavHost.kt
@@ -23,8 +23,25 @@
 /**
  * Construct a new [androidx.navigation.NavGraph] that supports dynamic navigation destinations
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your dynamic NavGraph instead",
+    ReplaceWith(
+        "createGraph(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun NavHost.createGraph(
     @IdRes id: Int = 0,
     @IdRes startDestination: Int,
     builder: DynamicNavGraphBuilder.() -> Unit
 ): NavGraph = navController.createGraph(id, startDestination, builder)
+
+/**
+ * Construct a new [androidx.navigation.NavGraph] that supports dynamic navigation destinations
+ */
+public inline fun NavHost.createGraph(
+    startDestination: String,
+    route: String? = null,
+    builder: DynamicNavGraphBuilder.() -> Unit
+): NavGraph = navController.createGraph(startDestination, route, builder)
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt
index 74bc039..0502397 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt
@@ -23,12 +23,12 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class DynamicExtrasTest {
+public class DynamicExtrasTest {
 
-    class TestNavigatorExtras : Navigator.Extras
+    public class TestNavigatorExtras : Navigator.Extras
 
     @Test
-    fun build_withMonitorAndExtras() {
+    public fun build_withMonitorAndExtras() {
         val monitor = DynamicInstallMonitor()
         val navExtras = TestNavigatorExtras()
         val extras = DynamicExtras(monitor, navExtras)
@@ -37,7 +37,7 @@
     }
 
     @Test
-    fun build_withoutMonitorOrExtras() {
+    public fun build_withoutMonitorOrExtras() {
         val extras = DynamicExtras()
         assertThat(extras.destinationExtras).isNull()
         assertThat(extras.installMonitor).isNull()
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt
index f62db26..cc94ec6 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt
@@ -27,7 +27,7 @@
 import org.mockito.Mockito.mock
 
 @RunWith(JUnit4::class)
-class DynamicGraphNavigatorTest {
+public class DynamicGraphNavigatorTest {
 
     private val navigator =
         DynamicGraphNavigator(
@@ -36,12 +36,12 @@
         )
 
     @Test
-    fun testCreateDestination() {
+    public fun testCreateDestination() {
         assertNotNull(navigator.createDestination())
     }
 
     @Test
-    fun testInstallDefaultProgressDestination() {
+    public fun testInstallDefaultProgressDestination() {
         val navDestination = mock(NavDestination::class.java)
         navigator.installDefaultProgressDestination { navDestination }
         assertEquals(navDestination, navigator.defaultProgressDestinationSupplier!!.invoke())
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
index 72ca243..d54950a 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
@@ -30,7 +30,7 @@
 import org.mockito.Mockito.spy
 
 @RunWith(JUnit4::class)
-class DynamicInstallManagerTest {
+public class DynamicInstallManagerTest {
 
     private val splitInstallManager = mock(SplitInstallManager::class.java)
     private var manager = DynamicInstallManager(
@@ -39,13 +39,13 @@
     )
 
     @Test
-    fun testNeedsInstall_InstallNeeded() {
+    public fun testNeedsInstall_InstallNeeded() {
         mockWhen(splitInstallManager.installedModules).thenReturn(setOf("not-module"))
         assertTrue(manager.needsInstall("module"))
     }
 
     @Test
-    fun testNeedsInstall_NoInstallNeeded() {
+    public fun testNeedsInstall_NoInstallNeeded() {
         mockWhen(splitInstallManager.installedModules).thenReturn(setOf("module"))
         assertFalse(manager.needsInstall("module"))
     }
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt
index caed9ac..b6046f5 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt
@@ -26,9 +26,9 @@
 import org.mockito.Mockito.verify
 
 @RunWith(JUnit4::class)
-class DynamicInstallMonitorTest {
+public class DynamicInstallMonitorTest {
     @Test
-    fun testCancelInstall_sessionIdZero() {
+    public fun testCancelInstall_sessionIdZero() {
         val monitor = DynamicInstallMonitor()
         val manager = mock(SplitInstallManager::class.java)
 
@@ -39,7 +39,7 @@
     }
 
     @Test
-    fun testCancelInstall_sessionIdNotZero() {
+    public fun testCancelInstall_sessionIdNotZero() {
         val monitor = DynamicInstallMonitor()
         val manager = mock(SplitInstallManager::class.java)
 
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt
index f48fdbb..4d48ad3 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt
@@ -32,10 +32,10 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class DynamicNavGraphTest {
+public class DynamicNavGraphTest {
 
     @get:Rule
-    val instantTaskExecutorRule = InstantTaskExecutorRule()
+    public val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()
 
     private val progressId = 1
     private lateinit var provider: NavigatorProvider
@@ -46,7 +46,7 @@
     private lateinit var noOpNavigator: NoOpNavigator
 
     @Before
-    fun setup() {
+    public fun setup() {
         provider = NavigatorProvider()
         noOpNavigator = NoOpNavigator()
         navigator = DynamicGraphNavigator(
@@ -60,12 +60,12 @@
     }
 
     @Test(expected = IllegalStateException::class)
-    fun testGetOrThrow_NoParent() {
+    public fun testGetOrThrow_NoParent() {
         DynamicNavGraph.getOrThrow(noOpNavigator.createDestination())
     }
 
     @Test
-    fun testGetOrThrow_CorrectParent() {
+    public fun testGetOrThrow_CorrectParent() {
         setupProgressDestination(
             noOpNavigator.createDestination().apply {
                 id = progressId
@@ -81,13 +81,13 @@
     }
 
     @Test(expected = IllegalStateException::class)
-    fun testNavigateToProgressDestination_withoutProgressDestination() {
+    public fun testNavigateToProgressDestination_withoutProgressDestination() {
         setupProgressDestination(null)
         navigator.navigateToProgressDestination(dynamicNavGraph, null)
     }
 
     @Test
-    fun testNavigateToProgressDestination_withProviderAndDestination() {
+    public fun testNavigateToProgressDestination_withProviderAndDestination() {
         setupProgressDestination(
             noOpNavigator.createDestination().apply {
                 id = progressId
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt
index e511f56..cbdf11a 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt
@@ -24,7 +24,7 @@
 /**
  * A dynamic install manager used for testing.
  */
-class TestDynamicInstallManager :
+public class TestDynamicInstallManager :
     DynamicInstallManager(
         Mockito.spy(Context::class.java),
         Mockito.mock(SplitInstallManager::class.java)
diff --git a/navigation/navigation-fragment-ktx/build.gradle b/navigation/navigation-fragment-ktx/build.gradle
index b73a493..ea68cc8 100644
--- a/navigation/navigation-fragment-ktx/build.gradle
+++ b/navigation/navigation-fragment-ktx/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
diff --git a/navigation/navigation-fragment/api/current.txt b/navigation/navigation-fragment/api/current.txt
index ce7ec82..dd116f0 100644
--- a/navigation/navigation-fragment/api/current.txt
+++ b/navigation/navigation-fragment/api/current.txt
@@ -23,13 +23,16 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
-    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
     method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
   }
 
   public final class DialogFragmentNavigatorDestinationBuilderKt {
     method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
     method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
   }
 
   public final class FragmentKt {
@@ -67,7 +70,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
-    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
     ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
     method public androidx.navigation.fragment.FragmentNavigator.Destination build();
   }
diff --git a/navigation/navigation-fragment/api/public_plus_experimental_current.txt b/navigation/navigation-fragment/api/public_plus_experimental_current.txt
index ce7ec82..dd116f0 100644
--- a/navigation/navigation-fragment/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-fragment/api/public_plus_experimental_current.txt
@@ -23,13 +23,16 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
-    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
     method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
   }
 
   public final class DialogFragmentNavigatorDestinationBuilderKt {
     method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
     method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
   }
 
   public final class FragmentKt {
@@ -67,7 +70,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
-    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
     ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
     method public androidx.navigation.fragment.FragmentNavigator.Destination build();
   }
diff --git a/navigation/navigation-fragment/api/restricted_current.txt b/navigation/navigation-fragment/api/restricted_current.txt
index ce7ec82..dd116f0 100644
--- a/navigation/navigation-fragment/api/restricted_current.txt
+++ b/navigation/navigation-fragment/api/restricted_current.txt
@@ -23,13 +23,16 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
-    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
     method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
   }
 
   public final class DialogFragmentNavigatorDestinationBuilderKt {
     method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
     method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
   }
 
   public final class FragmentKt {
@@ -67,7 +70,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
-    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
     ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
     method public androidx.navigation.fragment.FragmentNavigator.Destination build();
   }
diff --git a/navigation/navigation-fragment/build.gradle b/navigation/navigation-fragment/build.gradle
index 5837d97..72e3ba0 100644
--- a/navigation/navigation-fragment/build.gradle
+++ b/navigation/navigation-fragment/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -30,14 +27,14 @@
     api(projectOrArtifact(":fragment:fragment-ktx"))
     api(project(":navigation:navigation-runtime"))
 
-    api(KOTLIN_STDLIB)
+    api(libs.kotlinStdlib)
     androidTestImplementation(project(":navigation:navigation-testing"))
     androidTestImplementation(projectOrArtifact(":fragment:fragment-testing"))
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.fragment", module: "fragment"
     })
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilderTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilderTest.kt
index fed4875..7532fdb 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilderTest.kt
@@ -36,6 +36,7 @@
     val activityRule = androidx.test.rule.ActivityTestRule<TestActivity>(TestActivity::class.java)
     private val fragmentManager get() = activityRule.activity.supportFragmentManager
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test fun fragment() {
         val navHostFragment = NavHostFragment()
@@ -53,6 +54,7 @@
             .isEqualTo(BuilderTestDialogFragment::class.java.name)
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test fun fragmentWithBody() {
         val navHostFragment = NavHostFragment()
@@ -74,8 +76,48 @@
             .that(graph[DESTINATION_ID].label)
             .isEqualTo(LABEL)
     }
+
+    @UiThreadTest
+    @Test fun fragmentRoute() {
+        val navHostFragment = NavHostFragment()
+        fragmentManager.beginTransaction()
+            .add(android.R.id.content, navHostFragment)
+            .commitNow()
+        val graph = navHostFragment.createGraph(startDestination = DESTINATION_ROUTE) {
+            dialog<BuilderTestDialogFragment>(DESTINATION_ROUTE)
+        }
+        assertWithMessage("Destination should be added to the graph")
+            .that(DESTINATION_ROUTE in graph)
+            .isTrue()
+        assertWithMessage("DialogFragment class should be set to BuilderTestDialogFragment")
+            .that((graph[DESTINATION_ROUTE] as DialogFragmentNavigator.Destination).className)
+            .isEqualTo(BuilderTestDialogFragment::class.java.name)
+    }
+
+    @UiThreadTest
+    @Test fun fragmentWithBodyRoute() {
+        val navHostFragment = NavHostFragment()
+        fragmentManager.beginTransaction()
+            .add(android.R.id.content, navHostFragment)
+            .commitNow()
+        val graph = navHostFragment.createGraph(startDestination = DESTINATION_ROUTE) {
+            dialog<BuilderTestDialogFragment>(DESTINATION_ROUTE) {
+                label = LABEL
+            }
+        }
+        assertWithMessage("Destination should be added to the graph")
+            .that(DESTINATION_ROUTE in graph)
+            .isTrue()
+        assertWithMessage("DialogFragment class should be set to BuilderTestDialogFragment")
+            .that((graph[DESTINATION_ROUTE] as DialogFragmentNavigator.Destination).className)
+            .isEqualTo(BuilderTestDialogFragment::class.java.name)
+        assertWithMessage("DialogFragment should have label set")
+            .that(graph[DESTINATION_ROUTE].label)
+            .isEqualTo(LABEL)
+    }
 }
 
 private const val DESTINATION_ID = 1
+private const val DESTINATION_ROUTE = "destination"
 private const val LABEL = "Test"
 class BuilderTestDialogFragment : DialogFragment()
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilderTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilderTest.kt
index 6e28a13..616174b 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilderTest.kt
@@ -37,6 +37,7 @@
     val activityRule = androidx.test.rule.ActivityTestRule<TestActivity>(TestActivity::class.java)
     private val fragmentManager get() = activityRule.activity.supportFragmentManager
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test fun fragment() {
         val navHostFragment = NavHostFragment()
@@ -57,6 +58,7 @@
         )
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test fun fragmentWithBody() {
         val navHostFragment = NavHostFragment()
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
index f3c50e7..73b3b9bf 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
@@ -56,6 +56,7 @@
             navigatorProvider += TestNavigator()
         }
 
+    @Suppress("DEPRECATION")
     @Test
     fun vmInitialization() {
         val scenario = launchFragmentInContainer<TestVMFragment>()
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt
index 465cf75..cf58061 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt
@@ -27,6 +27,11 @@
 /**
  * Construct a new [DialogFragmentNavigator.Destination]
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your DialogFragmentDestination instead",
+    ReplaceWith("dialog<F>(route = id.toString())")
+)
 public inline fun <reified F : DialogFragment> NavGraphBuilder.dialog(
     @IdRes id: Int
 ): Unit = dialog<F>(id) {}
@@ -34,6 +39,11 @@
 /**
  * Construct a new [DialogFragmentNavigator.Destination]
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your DialogFragmentDestination instead",
+    ReplaceWith("dialog<F>(route = id.toString()) { builder.invoke() }")
+)
 public inline fun <reified F : DialogFragment> NavGraphBuilder.dialog(
     @IdRes id: Int,
     builder: DialogFragmentNavigatorDestinationBuilder.() -> Unit
@@ -44,16 +54,59 @@
         F::class
     ).apply(builder)
 )
+/**
+ * Construct a new [DialogFragmentNavigator.Destination]
+ */
+public inline fun <reified F : DialogFragment> NavGraphBuilder.dialog(
+    route: String
+): Unit = dialog<F>(route) {}
+
+/**
+ * Construct a new [DialogFragmentNavigator.Destination]
+ */
+public inline fun <reified F : DialogFragment> NavGraphBuilder.dialog(
+    route: String,
+    builder: DialogFragmentNavigatorDestinationBuilder.() -> Unit
+): Unit = destination(
+    DialogFragmentNavigatorDestinationBuilder(
+        provider[DialogFragmentNavigator::class],
+        route,
+        F::class
+    ).apply(builder)
+)
 
 /**
  * DSL for constructing a new [DialogFragmentNavigator.Destination]
  */
 @NavDestinationDsl
-public class DialogFragmentNavigatorDestinationBuilder(
-    navigator: DialogFragmentNavigator,
-    @IdRes id: Int,
-    private val fragmentClass: KClass<out DialogFragment>
-) : NavDestinationBuilder<DialogFragmentNavigator.Destination>(navigator, id) {
+public class DialogFragmentNavigatorDestinationBuilder :
+    NavDestinationBuilder<DialogFragmentNavigator.Destination> {
+
+    private var fragmentClass: KClass<out DialogFragment>
+
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to build your DialogFragmentNavigatorDestination instead",
+        ReplaceWith(
+            "DialogFragmentNavigatorDestinationBuilder(navigator, route = id.toString(), " +
+                "fragmentClass) "
+        )
+    )
+    public constructor(
+        navigator: DialogFragmentNavigator,
+        @IdRes id: Int,
+        fragmentClass: KClass<out DialogFragment>
+    ) : super(navigator, id) {
+        this.fragmentClass = fragmentClass
+    }
+
+    public constructor(
+        navigator: DialogFragmentNavigator,
+        route: String,
+        fragmentClass: KClass<out DialogFragment>
+    ) : super(navigator, route) {
+        this.fragmentClass = fragmentClass
+    }
 
     override fun build(): DialogFragmentNavigator.Destination =
         super.build().also { destination ->
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilder.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilder.kt
index 60eda99..e0060d9 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilder.kt
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilder.kt
@@ -27,6 +27,11 @@
 /**
  * Construct a new [FragmentNavigator.Destination]
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your FragmentDestination instead",
+    ReplaceWith("fragment<F>(route = id.toString())")
+)
 public inline fun <reified F : Fragment> NavGraphBuilder.fragment(
     @IdRes id: Int
 ): Unit = fragment<F>(id) {}
@@ -34,6 +39,11 @@
 /**
  * Construct a new [FragmentNavigator.Destination]
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your FragmentDestination instead",
+    ReplaceWith("fragment<F>(route = id.toString()) { builder.invoke() }")
+)
 public inline fun <reified F : Fragment> NavGraphBuilder.fragment(
     @IdRes id: Int,
     builder: FragmentNavigatorDestinationBuilder.() -> Unit
@@ -75,6 +85,13 @@
 
     private var fragmentClass: KClass<out Fragment>
 
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to build your FragmentNavigatorDestination instead",
+        ReplaceWith(
+            "FragmentNavigatorDestinationBuilder(navigator, route = id.toString(), fragmentClass) "
+        )
+    )
     public constructor(
         navigator: FragmentNavigator,
         @IdRes id: Int,
diff --git a/navigation/navigation-runtime-ktx/build.gradle b/navigation/navigation-runtime-ktx/build.gradle
index ddb869e..5beadd4 100644
--- a/navigation/navigation-runtime-ktx/build.gradle
+++ b/navigation/navigation-runtime-ktx/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
diff --git a/navigation/navigation-runtime-truth/build.gradle b/navigation/navigation-runtime-truth/build.gradle
index dca17f9..683040f 100644
--- a/navigation/navigation-runtime-truth/build.gradle
+++ b/navigation/navigation-runtime-truth/build.gradle
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-
-import androidx.build.AndroidXExtension
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -30,17 +25,17 @@
 
 dependencies {
     api(project(":navigation:navigation-runtime"))
-    api(TRUTH)
-    api(KOTLIN_STDLIB)
+    api(libs.truth)
+    api(libs.kotlinStdlib)
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation(project(":internal-testutils-navigation"), {
         exclude group: "androidx.navigation", module: "navigation-common"
     })
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
 }
 
 androidx {
diff --git a/navigation/navigation-runtime/api/current.txt b/navigation/navigation-runtime/api/current.txt
index 03eef05..33808c0 100644
--- a/navigation/navigation-runtime/api/current.txt
+++ b/navigation/navigation-runtime/api/current.txt
@@ -59,7 +59,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
-    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
     ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
     method public androidx.navigation.ActivityNavigator.Destination build();
     method public String? getAction();
@@ -80,7 +80,7 @@
   }
 
   public final class ActivityNavigatorDestinationBuilderKt {
-    method public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
   }
 
@@ -152,7 +152,7 @@
   }
 
   public final class NavControllerKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
@@ -189,7 +189,7 @@
   }
 
   public final class NavHostKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
diff --git a/navigation/navigation-runtime/api/public_plus_experimental_current.txt b/navigation/navigation-runtime/api/public_plus_experimental_current.txt
index f98c73a..1a5eb2b 100644
--- a/navigation/navigation-runtime/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-runtime/api/public_plus_experimental_current.txt
@@ -61,7 +61,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
-    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
     ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
     method public androidx.navigation.ActivityNavigator.Destination build();
     method public String? getAction();
@@ -82,7 +82,7 @@
   }
 
   public final class ActivityNavigatorDestinationBuilderKt {
-    method public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
   }
 
@@ -166,7 +166,7 @@
   }
 
   public final class NavControllerKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
@@ -203,7 +203,7 @@
   }
 
   public final class NavHostKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
diff --git a/navigation/navigation-runtime/api/restricted_current.txt b/navigation/navigation-runtime/api/restricted_current.txt
index 03eef05..33808c0 100644
--- a/navigation/navigation-runtime/api/restricted_current.txt
+++ b/navigation/navigation-runtime/api/restricted_current.txt
@@ -59,7 +59,7 @@
   }
 
   @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
-    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
     ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
     method public androidx.navigation.ActivityNavigator.Destination build();
     method public String? getAction();
@@ -80,7 +80,7 @@
   }
 
   public final class ActivityNavigatorDestinationBuilderKt {
-    method public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
     method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
   }
 
@@ -152,7 +152,7 @@
   }
 
   public final class NavControllerKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
@@ -189,7 +189,7 @@
   }
 
   public final class NavHostKt {
-    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
     method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
   }
 
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index d395673..1ba297e 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-
-import androidx.build.AndroidXExtension
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -33,20 +29,20 @@
     api("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1")
     api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
 
-    api(KOTLIN_STDLIB)
+    api(libs.kotlinStdlib)
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
     androidTestImplementation(project(":internal-testutils-navigation"))
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_EXT_TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(ESPRESSO_INTENTS)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
-    androidTestImplementation(KOTLIN_STDLIB)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testExtTruth)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.espressoIntents)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
+    androidTestImplementation(libs.kotlinStdlib)
 }
 
 androidx {
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
index 53c3aea..6e1ae7a 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
@@ -32,6 +32,7 @@
     private val navController =
         NavController(ApplicationProvider.getApplicationContext() as android.content.Context)
 
+    @Suppress("DEPRECATION")
     @Test
     fun activity() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
@@ -50,6 +51,7 @@
         )
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun activityPackage() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
@@ -65,6 +67,7 @@
             .isEqualTo(PACKAGE_NAME)
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun activityClass() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
@@ -83,6 +86,7 @@
         )
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun action() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
@@ -101,6 +105,7 @@
         )
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun data() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
@@ -119,6 +124,7 @@
         )
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun dataPattern() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryLifecycleTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryLifecycleTest.kt
index 1c9b115..f276f45 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryLifecycleTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryLifecycleTest.kt
@@ -43,6 +43,7 @@
     /**
      * Test that navigating between siblings correctly stops the previous sibling.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycle() {
@@ -105,6 +106,7 @@
      * Test that navigating from a sibling to a FloatingWindow sibling leaves the previous
      * destination started.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleWithDialog() {
@@ -167,6 +169,7 @@
      * Test that navigating from within a nested navigation graph to one of the graph's
      * siblings correctly stops both the previous destination and its graph.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNested() {
@@ -232,6 +235,7 @@
      * FloatingWindow siblings correctly moves both the previous destination and its graph to
      * started.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNestedWithDialog() {
@@ -299,6 +303,7 @@
      * Test that navigating from within a nested navigation graph to one of the graph's
      * siblings correctly stops both the previous destination and its graph.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNestedOrdering() {
@@ -394,6 +399,7 @@
      * FloatingWindow siblings correctly moves both the previous destination and its graph to
      * started.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNestedOrderingWithDialog() {
@@ -476,6 +482,7 @@
      * Test that popping the last destination in a graph while navigating to a new
      * destination in that graph keeps the graph around
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleReplaceLastDestination() {
@@ -533,6 +540,7 @@
      * Test that popping the last destination in a graph while navigating correctly
      * cleans up the previous navigation graph
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleOrphanedGraph() {
@@ -590,6 +598,7 @@
      * Test that navigating to a new instance of a graph leaves the previous instance in its
      * current state.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNestedRepeated() {
@@ -663,6 +672,7 @@
      * Test that navigating to a new instance of a graph back to back with its previous
      * instance creates a brand new graph instance
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNestedRepeatedBackToBack() {
@@ -722,6 +732,7 @@
      * last destination from the previous instance of the graph correctly cleans up
      * the orphaned graph and creates a new graph instance.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNestedRepeatedBackToBackWithOrphanedGraph() {
@@ -789,6 +800,7 @@
      * Test that navigating to a new instance of a graph via a deep link to a FloatingWindow
      * destination leaves the previous instance in its current state.
      */
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleNestedRepeatedWithDialog() {
@@ -861,6 +873,7 @@
             .isEqualTo(Lifecycle.State.RESUMED)
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testLifecycleToDestroyedWhenInitialized() {
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
index bcd0b19..b061ee6 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
@@ -42,6 +42,7 @@
 @RunWith(AndroidJUnit4::class)
 class NavBackStackEntryTest {
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testGetViewModelStoreOwner() {
@@ -61,6 +62,7 @@
         assertThat(store).isNotNull()
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testGetViewModelStoreOwnerAndroidViewModel() {
@@ -81,6 +83,7 @@
         assertThat(viewModel).isNotNull()
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testGetViewModelStoreOwnerSavedStateViewModel() {
@@ -116,6 +119,7 @@
         assertThat(restoredState).isEqualTo("test")
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testSaveRestoreGetViewModelStoreOwner() {
@@ -165,6 +169,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testGetViewModelStoreOwnerSameGraph() {
@@ -188,6 +193,7 @@
         assertThat(sameGraphOwner.viewModelStore).isSameInstanceAs(viewStore)
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testGetSavedStateHandleRestored() {
@@ -278,6 +284,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testOnClearedWhenHostCleared() {
@@ -304,6 +311,7 @@
             .isTrue()
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testOnClearedWhenPopped() {
@@ -338,6 +346,7 @@
             .isTrue()
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testOnClearedWhenHostClearedAfterSaveState() {
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index d3f9e3a..c2e4857 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -173,6 +173,7 @@
         navController.setGraph(R.navigation.nav_start_destination, args)
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testStartDestinationWithArgsProgrammatic() {
@@ -238,6 +239,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testSetViewModelStoreOwnerAfterGraphSet() {
@@ -260,6 +262,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testSetSameViewModelStoreOwnerAfterGraphSet() {
@@ -1977,6 +1980,7 @@
         navController.navigate(R.id.second_test)
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun createGraph() {
         val graph = navController.createGraph(startDestination = DESTINATION_ID) {
@@ -1988,7 +1992,7 @@
 
     @UiThreadTest
     @Test
-    @Suppress("EXPERIMENTAL_API_USAGE")
+    @Suppress("DEPRECATION", "EXPERIMENTAL_API_USAGE")
     fun currentBackStackEntryFlow() = runBlocking {
         navController.graph = navController.createGraph(startDestination = 1) {
             test(1)
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavDeepLinkBuilderTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavDeepLinkBuilderTest.kt
index 951a236..0b21a08 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavDeepLinkBuilderTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavDeepLinkBuilderTest.kt
@@ -105,6 +105,7 @@
         assertEquals("Expected one Intent", 1, taskStackBuilder.intentCount)
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun fromContextSetGraphProgrammatic() {
         val deepLinkBuilder = NavDeepLinkBuilder(targetContext)
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavHostTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavHostTest.kt
index 37ecb1e..5ef8394 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavHostTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavHostTest.kt
@@ -35,6 +35,7 @@
             get() = this@NavHostTest.navController
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun createGraph() {
         val graph = navHost.createGraph(startDestination = DESTINATION_ID) {
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/FloatingTestNavigator.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/FloatingTestNavigator.kt
index 689f7c6..94ea1b6 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/FloatingTestNavigator.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/FloatingTestNavigator.kt
@@ -59,6 +59,7 @@
 /**
  * DSL for constructing a new [TestNavigator.Destination] from a [FloatingTestNavigator].
  */
+@Suppress("DEPRECATION")
 @NavDestinationDsl
 class FloatingTestNavigatorDestinationBuilder(
     navigator: FloatingTestNavigator,
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt
index 1089b72..c0cc861 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt
@@ -28,6 +28,11 @@
 /**
  * Construct a new [ActivityNavigator.Destination]
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to build your ActivityDestination instead",
+    ReplaceWith("activity(route = id.toString()) { builder.invoke() }")
+)
 public inline fun NavGraphBuilder.activity(
     @IdRes id: Int,
     builder: ActivityNavigatorDestinationBuilder.() -> Unit
@@ -59,6 +64,11 @@
     NavDestinationBuilder<ActivityNavigator.Destination> {
     private var context: Context
 
+    @Suppress("Deprecation")
+    @Deprecated(
+        "Use routes to create your ActivityNavigatorDestinationBuilder instead",
+        ReplaceWith("ActivityNavigatorDestinationBuilder(navigator, route = id.toString())")
+    )
     public constructor(navigator: ActivityNavigator, @IdRes id: Int) : super(navigator, id) {
         context = navigator.context
     }
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index 24b4764..fc4bde0 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -1949,6 +1949,14 @@
  * @param startDestination the route for the start destination
  * @param builder the builder used to construct the graph
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your NavGraph instead",
+    ReplaceWith(
+        "createGraph(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun NavController.createGraph(
     @IdRes id: Int = 0,
     @IdRes startDestination: Int,
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavHost.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavHost.kt
index bb6b996..f3e159b 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavHost.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavHost.kt
@@ -53,6 +53,14 @@
 /**
  * Construct a new [NavGraph]
  */
+@Suppress("Deprecation")
+@Deprecated(
+    "Use routes to create your NavGraph instead",
+    ReplaceWith(
+        "createGraph(startDestination = startDestination.toString(), route = id.toString()) " +
+            "{ builder.invoke() }"
+    )
+)
 public inline fun NavHost.createGraph(
     @IdRes id: Int = 0,
     @IdRes startDestination: Int,
diff --git a/navigation/navigation-safe-args-generator/build.gradle b/navigation/navigation-safe-args-generator/build.gradle
index 22d143a..68d7bf3 100644
--- a/navigation/navigation-safe-args-generator/build.gradle
+++ b/navigation/navigation-safe-args-generator/build.gradle
@@ -19,23 +19,21 @@
 import androidx.build.SdkHelperKt
 import androidx.build.SupportConfig
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("kotlin")
 }
 
 dependencies {
-    implementation(XPP3)
-    implementation(XMLPULL)
-    implementation(KOTLIN_STDLIB)
+    implementation(libs.xpp3)
+    implementation(libs.xmlpull)
+    implementation(libs.kotlinStdlib)
 
-    implementation(JAVAPOET)
-    implementation(KOTLINPOET)
+    implementation(libs.javapoet)
+    implementation(libs.kotlinPoet)
 
-    testImplementation(JUNIT)
-    testImplementation(GOOGLE_COMPILE_TESTING)
+    testImplementation(libs.junit)
+    testImplementation(libs.googleCompileTesting)
     testImplementation(fileTree(
             dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
             include : "android.jar"
diff --git a/navigation/navigation-safe-args-gradle-plugin/build.gradle b/navigation/navigation-safe-args-gradle-plugin/build.gradle
index c327f2c..b647eed 100644
--- a/navigation/navigation-safe-args-gradle-plugin/build.gradle
+++ b/navigation/navigation-safe-args-gradle-plugin/build.gradle
@@ -18,8 +18,6 @@
 import androidx.build.LibraryType
 import androidx.build.SdkResourceGenerator
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("kotlin")
@@ -27,14 +25,14 @@
 }
 
 dependencies {
-    implementation("com.android.tools.build:gradle:4.0.1")
-    implementation(KOTLIN_GRADLE_PLUGIN)
+    implementation("com.android.tools.build:gradle:4.2.0")
+    implementation(libs.kotlinGradlePlugin)
     api(project(":navigation:navigation-safe-args-generator"))
     api(gradleApi())
-    implementation(GSON)
+    implementation(libs.gson)
     testImplementation(gradleTestKit())
     testImplementation(project(":internal-testutils-gradle-plugin"))
-    testImplementation(JUNIT)
+    testImplementation(libs.junit)
 }
 
 SdkResourceGenerator.generateForHostTest(project)
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
index a66edb1..3dbcbf5 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
@@ -24,11 +24,10 @@
 import org.gradle.api.GradleException
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.ProjectLayout
+import org.gradle.api.provider.Property
 import org.gradle.api.provider.Provider
-import org.gradle.api.resources.TextResource
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.TaskAction
 import org.gradle.work.ChangeType
@@ -44,11 +43,8 @@
     @get:Input
     lateinit var rFilePackage: Provider<String>
 
-    @get:Internal
-    var applicationIdResource: TextResource? = null // null on AGP 3.2.1 and below
-
-    @get:Internal
-    var applicationId: String? = null // null on AGP 3.3.0 and above
+    @get:Input
+    val applicationId: Property<String> = project.objects.property(String::class.java)
 
     @get:Input
     var useAndroidX: Boolean = true
@@ -66,18 +62,10 @@
     @get:OutputDirectory
     lateinit var incrementalFolder: File
 
-    /**
-     * Gets the app id from either the [applicationIdResource] if available or [applicationId].
-     * The availability from which the app id string is retrieved from is based on the Android
-     * Gradle Plugin version of the project.
-     */
-    @Input
-    fun getApplicationIdResourceString() = applicationIdResource?.asString() ?: applicationId
-
     private fun generateArgs(navFiles: Collection<File>, out: File) = navFiles.map { file ->
         val output = SafeArgsGenerator(
             rFilePackage = rFilePackage.get(),
-            applicationId = getApplicationIdResourceString() ?: "",
+            applicationId = applicationId.get() ?: "",
             navigationXml = file,
             outputDir = out,
             useAndroidX = useAndroidX,
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
index ef3a3e8..dc02ba6 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
@@ -16,6 +16,9 @@
 
 package androidx.navigation.safeargs.gradle
 
+import com.android.build.api.extension.AndroidComponentsExtension
+import com.android.build.api.extension.ApplicationAndroidComponentsExtension
+import com.android.build.api.extension.DynamicFeatureAndroidComponentsExtension
 import com.android.build.gradle.AppExtension
 import com.android.build.gradle.BaseExtension
 import com.android.build.gradle.LibraryExtension
@@ -24,6 +27,7 @@
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.file.FileCollection
+import org.gradle.api.provider.Property
 import org.gradle.api.provider.ProviderFactory
 import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
 import java.io.File
@@ -62,12 +66,29 @@
                 "androidx.navigation.safeargs.kotlin plugin must be used with kotlin plugin"
             )
         }
+        val applicationIds = mutableMapOf<String, Property<String>>()
+        val variantExtension =
+            project.extensions.findByType(AndroidComponentsExtension::class.java)
+                ?: throw GradleException("safeargs plugin must be used with android plugin")
+        variantExtension.onVariants {
+            when (it) {
+                is ApplicationAndroidComponentsExtension, is
+                DynamicFeatureAndroidComponentsExtension ->
+                    applicationIds.getOrPut(it.name) {
+                        project.objects.property(String::class.java)
+                    }.value(it.applicationId)
+            }
+        }
         forEachVariant(extension) { variant ->
             val task = project.tasks.create(
                 "generateSafeArgs${variant.name.capitalize()}",
                 ArgumentsGenerationTask::class.java
             ) { task ->
-                setApplicationId(task, variant)
+                task.applicationId.set(
+                    applicationIds.getOrPut(variant.name) {
+                        project.objects.property(String::class.java)
+                    }.value(variant.applicationId)
+                )
                 task.rFilePackage = variant.rFilePackage()
                 task.navigationFiles = navigationFiles(variant, project)
                 task.outputDir = File(project.buildDir, "$GENERATED_PATH/${variant.dirName}")
@@ -81,24 +102,10 @@
                 }
                 task.generateKotlin = generateKotlin
             }
-            task.applicationIdResource?.let { task.dependsOn(it) }
             variant.registerJavaGeneratingTask(task, task.outputDir)
         }
     }
 
-    /**
-     * Sets the android project application id into the task.
-     */
-    private fun setApplicationId(task: ArgumentsGenerationTask, variant: BaseVariant) {
-        val appIdTextResource = variant.applicationIdTextResource
-        if (appIdTextResource != null) {
-            task.applicationIdResource = appIdTextResource
-        } else {
-            // getApplicationIdTextResource() returned null, fallback to getApplicationId()
-            task.applicationId = variant.applicationId
-        }
-    }
-
     private fun BaseVariant.rFilePackage() = providerFactory.provider {
         val mainSourceSet = sourceSets.find { it.name == "main" }
         val sourceSet = mainSourceSet ?: sourceSets[0]
diff --git a/navigation/navigation-testing/build.gradle b/navigation/navigation-testing/build.gradle
index 139c664..6404f62 100644
--- a/navigation/navigation-testing/build.gradle
+++ b/navigation/navigation-testing/build.gradle
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-
-import androidx.build.AndroidXExtension
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -34,12 +30,12 @@
     androidTestImplementation(project(":internal-testutils-navigation"), {
         exclude group: "androidx.navigation", module: "navigation-common"
     })
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_EXT_TRUTH)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testExtTruth)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
 }
 
 androidx {
diff --git a/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavHostControllerTest.kt b/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavHostControllerTest.kt
index 2612a35..76aa369 100644
--- a/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavHostControllerTest.kt
+++ b/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavHostControllerTest.kt
@@ -76,6 +76,7 @@
         assertThat(backStack[1].destination).isInstanceOf(TestNavigator.Destination::class.java)
     }
 
+    @Suppress("DEPRECATION")
     @UiThreadTest
     @Test
     fun testDsl() {
diff --git a/navigation/navigation-ui-ktx/build.gradle b/navigation/navigation-ui-ktx/build.gradle
index f2b8964..a45c3cf 100644
--- a/navigation/navigation-ui-ktx/build.gradle
+++ b/navigation/navigation-ui-ktx/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
diff --git a/navigation/navigation-ui/build.gradle b/navigation/navigation-ui/build.gradle
index e0fa19fa..60c9b7a 100644
--- a/navigation/navigation-ui/build.gradle
+++ b/navigation/navigation-ui/build.gradle
@@ -16,10 +16,7 @@
 
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
 plugins {
@@ -48,12 +45,12 @@
     androidTestImplementation(project(":internal-testutils-navigation"), {
         exclude group: "androidx.navigation", module: "navigation-common"
     })
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(MULTIDEX)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.multidex)
 }
 
 androidx {
diff --git a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
index 1f393b1..c2576ae 100644
--- a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
+++ b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
@@ -42,6 +42,7 @@
         context = InstrumentationRegistry.getInstrumentation().targetContext
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun testTopLevelFromGraph() {
         val navGraph = NavController(context).apply {
diff --git a/playground-common/playground-build.gradle b/playground-common/playground-build.gradle
index 0509722..b14459d7 100644
--- a/playground-common/playground-build.gradle
+++ b/playground-common/playground-build.gradle
@@ -55,7 +55,6 @@
     dependencies {
         classpath(libs.androidGradlePlugin)
         classpath(libs.kotlinGradlePlugin)
-        classpath(libs.kgpLeakPatcher)
         classpath(libs.kspGradlePlugin)
         classpath(libs.gson)
         classpath "androidx.build:gradle-plugin:0.1.0"
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 3e7dfa5..945474e 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,7 +26,7 @@
 kotlin.code.style=official
 # Disable docs
 androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=7378367
+androidx.playground.snapshotBuildId=7396899
 androidx.playground.metalavaBuildId=7255182
 androidx.playground.dokkaBuildId=7299536
 androidx.studio.type=playground
diff --git a/profileinstaller/integration-tests/init-macrobenchmark-target/README.md b/profileinstaller/integration-tests/init-macrobenchmark-target/README.md
new file mode 100644
index 0000000..1bed279
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark-target/README.md
@@ -0,0 +1 @@
+Benchmark startup overhead introduced by profileinstaller's use of startup library
\ No newline at end of file
diff --git a/profileinstaller/integration-tests/init-macrobenchmark-target/build.gradle b/profileinstaller/integration-tests/init-macrobenchmark-target/build.gradle
new file mode 100644
index 0000000..411e5df
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark-target/build.gradle
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("kotlin-android")
+}
+
+android {
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
+        }
+    }
+}
+
+dependencies {
+    implementation(KOTLIN_STDLIB)
+    implementation(project(":profileinstaller:profileinstaller"))
+}
diff --git a/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/AndroidManifest.xml b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f2fbc15
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<!--
+  ~ Copyright 2020 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
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.profileinstaller.integration.macrobenchmark.target">
+
+    <application
+        android:label="Profileinstaller Macrobenchmark Target"
+        android:allowBackup="false"
+        android:supportsRtl="true"
+        tools:ignore="MissingApplicationIcon">
+
+        <!-- Profileable to enable macrobenchmark profiling -->
+        <!--suppress AndroidElementNotAllowed -->
+        <profileable android:shell="true"/>
+
+        <!--
+        Activities need to be exported so the macrobenchmark can discover them
+        under the new package visibility changes for Android 11.
+         -->
+        <activity
+            android:name=".SimpleTextActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="profileinstaller.init.macrobenchmark.TARGET" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/java/androidx/profileinstaller/integration/macrobenchmark/target/SimpleTextActivity.kt b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/java/androidx/profileinstaller/integration/macrobenchmark/target/SimpleTextActivity.kt
new file mode 100644
index 0000000..c19d537
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/java/androidx/profileinstaller/integration/macrobenchmark/target/SimpleTextActivity.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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.profileinstaller.integration.macrobenchmark.target
+
+import android.app.Activity
+import android.os.Bundle
+import android.widget.TextView
+
+class SimpleTextActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main)
+
+        val notice = findViewById<TextView>(R.id.txtNotice)
+        notice.setText(R.string.app_notice)
+    }
+}
diff --git a/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/res/layout/activity_main.xml b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..42146bc
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright 2020 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/txtNotice"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="50sp"
+        tools:text="profileinstaller benchmark️" />
+
+</FrameLayout>
diff --git a/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/res/values/donottranslate-strings.xml b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/res/values/donottranslate-strings.xml
new file mode 100644
index 0000000..c2c1ed3
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark-target/src/main/res/values/donottranslate-strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2020 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.
+  -->
+
+<resources>
+    <string name="app_notice">profileinstaller macrobenchmark app.</string>
+</resources>
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/build.gradle b/profileinstaller/integration-tests/init-macrobenchmark/build.gradle
new file mode 100644
index 0000000..33e00c2
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark/build.gradle
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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 org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 28
+    }
+}
+
+dependencies {
+    androidTestImplementation(project(":profileinstaller:profileinstaller"))
+    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
+    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+}
+
+def installReleaseTarget = tasks.getByPath(
+        ":profileinstaller:integration-tests:init-macrobenchmark-target:installRelease"
+)
+
+// Define a task dependency so the app is installed before we run macro benchmarks.
+tasks.getByPath(":profileinstaller:integration-tests:init-macrobenchmark:connectedCheck")
+    .dependsOn(installReleaseTarget)
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/AndroidManifest.xml b/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..c1bd315
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.profileinstaller.integration.macrobenchmark.test"/>
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt b/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
new file mode 100644
index 0000000..83d0a926
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 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.profileinstaller.integration.macrobenchmark
+
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.testutils.createStartupCompilationParams
+import androidx.testutils.measureStartup
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class ProfileinstallerStartupBenchmark(
+    private val startupMode: StartupMode,
+    private val compilationMode: CompilationMode
+) {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    @Test
+    fun startup() = benchmarkRule.measureStartup(
+        compilationMode = compilationMode,
+        startupMode = startupMode,
+        packageName = "androidx.profileinstaller.integration.macrobenchmark.target"
+    ) {
+        action = "profileinstaller.init.macrobenchmark.TARGET"
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "startup={0},compilation={1}")
+        @JvmStatic
+        fun parameters() = createStartupCompilationParams()
+    }
+}
\ No newline at end of file
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml b/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ccd5d59
--- /dev/null
+++ b/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2020 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.profileinstaller.integration.macrobenchmark" />
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/build.gradle b/profileinstaller/integration-tests/testapp/build.gradle
similarity index 100%
rename from profileinstaller/profileinstaller/integration-tests/testapp/build.gradle
rename to profileinstaller/integration-tests/testapp/build.gradle
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/cli/all_compose_profile.txt b/profileinstaller/integration-tests/testapp/cli/all_compose_profile.txt
similarity index 100%
rename from profileinstaller/profileinstaller/integration-tests/testapp/cli/all_compose_profile.txt
rename to profileinstaller/integration-tests/testapp/cli/all_compose_profile.txt
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/cli/build_bundle_launch.sh b/profileinstaller/integration-tests/testapp/cli/build_bundle_launch.sh
similarity index 91%
rename from profileinstaller/profileinstaller/integration-tests/testapp/cli/build_bundle_launch.sh
rename to profileinstaller/integration-tests/testapp/cli/build_bundle_launch.sh
index cfcb5dc..ad3ab2b 100755
--- a/profileinstaller/profileinstaller/integration-tests/testapp/cli/build_bundle_launch.sh
+++ b/profileinstaller/integration-tests/testapp/cli/build_bundle_launch.sh
@@ -19,20 +19,20 @@
 
 SCRIPT=`realpath $0`
 SCRIPT_DIR=`dirname $SCRIPT`
-SUPPORT_DIR=$SCRIPT_DIR/../../../../../
+SUPPORT_DIR=$SCRIPT_DIR/../../../../
 TMP_DIR=`mktemp -d`
 
 pushd $SUPPORT_DIR
 
 echo "===START=== Rebuilding apk..."
 ANDROIDX_PROJECTS=COMPOSE ./gradlew \
-  :profileinstaller:profileinstaller:integration-tests:testapp:clean
+  :profileinstaller:integration-tests:testapp:clean
 if [ $DEBUG = true ]; then
   ANDROIDX_PROJECTS=COMPOSE ./gradlew \
-    :profileinstaller:profileinstaller:integration-tests:testapp:assembleDebug
+    :profileinstaller:integration-tests:testapp:assembleDebug
 else
   ANDROIDX_PROJECTS=COMPOSE ./gradlew \
-    :profileinstaller:profileinstaller:integration-tests:testapp:assembleRelease
+    :profileinstaller:integration-tests:testapp:assembleRelease
 fi
 echo "===/DONE=== Rebuilding apk..."
 
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/cli/instructions.md b/profileinstaller/integration-tests/testapp/cli/instructions.md
similarity index 100%
rename from profileinstaller/profileinstaller/integration-tests/testapp/cli/instructions.md
rename to profileinstaller/integration-tests/testapp/cli/instructions.md
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/cli/profgen-cli.jar b/profileinstaller/integration-tests/testapp/cli/profgen-cli.jar
similarity index 100%
rename from profileinstaller/profileinstaller/integration-tests/testapp/cli/profgen-cli.jar
rename to profileinstaller/integration-tests/testapp/cli/profgen-cli.jar
Binary files differ
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/cli/repackage.py b/profileinstaller/integration-tests/testapp/cli/repackage.py
similarity index 94%
rename from profileinstaller/profileinstaller/integration-tests/testapp/cli/repackage.py
rename to profileinstaller/integration-tests/testapp/cli/repackage.py
index 9a5db31..3b30a3f 100755
--- a/profileinstaller/profileinstaller/integration-tests/testapp/cli/repackage.py
+++ b/profileinstaller/integration-tests/testapp/cli/repackage.py
@@ -14,13 +14,13 @@
 PATH_TO_APKSIGNER = 'apksigner'
 
 SCRIPT_PATH = Path(__file__).parent.absolute()
-SUPPORT_PATH = (SCRIPT_PATH / Path("../../../../..")).resolve()
+SUPPORT_PATH = (SCRIPT_PATH / Path("../../../..")).resolve()
 ROOT_DIR = (SUPPORT_PATH / Path("../..")).resolve()
 BUILD_OUT_DIR = (Path(SUPPORT_PATH) / Path(
-    "../../out/androidx/profileinstaller/profileinstaller/integration-tests/"
+    "../../out/androidx/profileinstaller/integration-tests/"
     "testapp/build/outputs/apk/")).resolve()
 MAPPING_OUT_PATH = (Path(SUPPORT_PATH) / Path(
-    "../../out/androidx/profileinstaller/profileinstaller/integration-tests/"
+    "../../out/androidx/profileinstaller/integration-tests/"
     "testapp/build/outputs/mapping/release/mapping.txt")).resolve()
 
 APK_PREFIX = "testapp"
@@ -119,6 +119,8 @@
     working_dir.mkdir()
     working_apk = working_dir / Path("working.apk")
     shutil.copyfile(apk_src, working_apk)
+    print(f"copying to {SUPPORT_PATH / Path('baseline.prof')}")
+    shutil.copyfile(profile, SUPPORT_PATH / Path("baseline.prof"))
     with ZipFile(working_apk, 'a') as zip:
         profile_destination = Path('assets/dexopt/') / Path(APK_PROFILE_FILE)
         if str(profile_destination) in [it.filename for it in zip.infolist()]:
@@ -155,4 +157,4 @@
     profile = profile_from(args.profile)
     jar = jar_from(args.jar)
     output_apk = output_apk_from(args.output)
-    generate_apk(apk_src, profile, jar, output_apk, args.debug, args.apk_signer)
\ No newline at end of file
+    generate_apk(apk_src, profile, jar, output_apk, args.debug, args.apk_signer)
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/src/main/AndroidManifest.xml b/profileinstaller/integration-tests/testapp/src/main/AndroidManifest.xml
similarity index 100%
rename from profileinstaller/profileinstaller/integration-tests/testapp/src/main/AndroidManifest.xml
rename to profileinstaller/integration-tests/testapp/src/main/AndroidManifest.xml
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/src/main/java/androidx/profileinstaller/integration/testapp/MainActivity.kt b/profileinstaller/integration-tests/testapp/src/main/java/androidx/profileinstaller/integration/testapp/MainActivity.kt
similarity index 100%
rename from profileinstaller/profileinstaller/integration-tests/testapp/src/main/java/androidx/profileinstaller/integration/testapp/MainActivity.kt
rename to profileinstaller/integration-tests/testapp/src/main/java/androidx/profileinstaller/integration/testapp/MainActivity.kt
diff --git a/profileinstaller/profileinstaller/integration-tests/testapp/OWNERS b/profileinstaller/profileinstaller/integration-tests/testapp/OWNERS
deleted file mode 100644
index 42abc4e..0000000
--- a/profileinstaller/profileinstaller/integration-tests/testapp/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-jellefresen@google.com
-pavlis@google.com
diff --git a/room/OWNERS b/room/OWNERS
index 3ea2934..6d57c2e 100644
--- a/room/OWNERS
+++ b/room/OWNERS
@@ -1,5 +1,6 @@
 danysantiago@google.com
 sergeyv@google.com
 yboyar@google.com
+dustinlam@google.com
 
-per-file settings.gradle = dustinlam@google.com, rahulrav@google.com
+per-file settings.gradle = rahulrav@google.com
diff --git a/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt b/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt
index 070e72a..271c1df 100644
--- a/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.preconditions
 
-import androidx.room.log.RLog
 import androidx.room.compiler.processing.XElement
+import androidx.room.log.RLog
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeVariableName
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 7bbb587..83381bc 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -624,7 +624,13 @@
     val INVALID_TABLE_NAME = "Invalid table name. Room does not allow using ` or \" in table names"
 
     val RAW_QUERY_BAD_PARAMS = "RawQuery methods should have 1 and only 1 parameter with type" +
-        " String or SupportSQLiteQuery"
+        " SupportSQLiteQuery"
+
+    fun parameterCannotBeNullable(
+        parameterName: String
+    ) = """
+        Parameter `$parameterName` cannot be nullable.
+    """.trimIndent()
 
     val RAW_QUERY_BAD_RETURN_TYPE = "RawQuery methods must return a non-void type."
 
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
index 4d2a30d..39377be8 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
@@ -22,6 +22,7 @@
 import androidx.room.ext.isEntityElement
 import androidx.room.parser.SqlParser
 import androidx.room.compiler.processing.XMethodElement
+import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XVariableElement
 import androidx.room.processor.ProcessorErrors.RAW_QUERY_STRING_PARAMETER_REMOVED
@@ -117,6 +118,17 @@
         if (extractParams.size == 1 && !executableElement.isVarArgs()) {
             val param = extractParams.first().asMemberOf(containing)
             val processingEnv = context.processingEnv
+            if (param.nullability == XNullability.NULLABLE) {
+                context.logger.e(
+                    element = extractParams.first(),
+                    msg = ProcessorErrors.parameterCannotBeNullable(
+                        parameterName = extractParams.first().name
+                    )
+                )
+            }
+            // use nullable type to catch bad nullability. Because it is non-null by default in
+            // KSP, assignability will fail and we'll print a generic error instead of a specific
+            // one
             val supportQueryType = processingEnv.requireType(SupportDbTypeNames.QUERY)
             val isSupportSql = supportQueryType.isAssignableFrom(param)
             if (isSupportSql) {
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
index 16dc991..9938afa4 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.writer
 
+import androidx.annotation.NonNull
 import androidx.room.ext.AndroidTypeNames
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
@@ -130,7 +131,7 @@
         val scope = CodeGenScope(this)
         return MethodSpec.methodBuilder("getRequiredAutoMigrationSpecs").apply {
             addAnnotation(Override::class.java)
-            addModifiers(PROTECTED)
+            addModifiers(PUBLIC)
             returns(
                 ParameterizedTypeName.get(
                     CommonTypeNames.SET,
@@ -349,13 +350,27 @@
 
     private fun getAutoMigrations(): MethodSpec {
         return MethodSpec.methodBuilder("getAutoMigrations").apply {
-            addModifiers(PROTECTED)
+            addModifiers(PUBLIC)
             addAnnotation(Override::class.java)
+            addParameter(
+                ParameterSpec.builder(
+                    ParameterizedTypeName.get(
+                        CommonTypeNames.MAP,
+                        ParameterizedTypeName.get(
+                            ClassName.get(Class::class.java),
+                            WildcardTypeName.subtypeOf(RoomTypeNames.AUTO_MIGRATION_SPEC)
+                        ),
+                        RoomTypeNames.AUTO_MIGRATION_SPEC
+                    ),
+                    "autoMigrationSpecsMap"
+                ).addAnnotation(NonNull::class.java).build()
+            )
+
             returns(ParameterizedTypeName.get(CommonTypeNames.LIST, RoomTypeNames.MIGRATION))
             val autoMigrationsList = database.autoMigrations.map { autoMigrationResult ->
                 if (autoMigrationResult.isSpecProvided) {
                     CodeBlock.of(
-                        "new $T(mAutoMigrationSpecs.get($T.class))",
+                        "new $T(autoMigrationSpecsMap.get($T.class))",
                         autoMigrationResult.implTypeName,
                         autoMigrationResult.specClassName
                     )
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
index 5dd84db..e9df8c9 100644
--- a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -1,5 +1,6 @@
 package foo.bar;
 
+import androidx.annotation.NonNull;
 import androidx.room.DatabaseConfiguration;
 import androidx.room.InvalidationTracker;
 import androidx.room.RoomOpenHelper;
@@ -188,13 +189,14 @@
     }
 
     @Override
-    protected Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
+    public Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
         final HashSet<Class<? extends AutoMigrationSpec>> _autoMigrationSpecsSet = new HashSet<Class<? extends AutoMigrationSpec>>();
         return _autoMigrationSpecsSet;
     }
 
     @Override
-    protected List<Migration> getAutoMigrations() {
+    public List<Migration> getAutoMigrations(
+            @NonNull Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> autoMigrationSpecsMap) {
         return Arrays.asList();
     }
 
diff --git a/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
index 7145423..88ea374 100644
--- a/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
@@ -286,6 +286,40 @@
     }
 
     @Test
+    fun badType() {
+        singleQueryMethod(
+            """
+                @RawQuery
+                abstract public int[] foo(int query);
+                """
+        ) { _, invocation ->
+            invocation.assertCompilationResult {
+                hasErrorContaining(
+                    ProcessorErrors.RAW_QUERY_BAD_PARAMS
+                )
+            }
+        }
+    }
+
+    @Test
+    fun badType_nullable() {
+        singleQueryMethod(
+            """
+                @RawQuery
+                abstract public int[] foo(@androidx.annotation.Nullable SupportSQLiteQuery query);
+                """
+        ) { _, invocation ->
+            invocation.assertCompilationResult {
+                hasErrorContaining(
+                    ProcessorErrors.parameterCannotBeNullable(
+                        parameterName = "query"
+                    )
+                )
+            }
+        }
+    }
+
+    @Test
     fun observed_notAnEntity() {
         singleQueryMethod(
             """
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
index faabb9d..8c72847 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
@@ -17,11 +17,11 @@
 package androidx.room.integration.kotlintestapp.migration
 
 import androidx.room.Room
+import androidx.room.RoomDatabase
 import androidx.room.migration.Migration
 import androidx.room.testing.MigrationTestHelper
 import androidx.room.util.TableInfo
 import androidx.sqlite.db.SupportSQLiteDatabase
-import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
 import androidx.test.filters.MediumTest
 import androidx.test.platform.app.InstrumentationRegistry
 import org.hamcrest.CoreMatchers.`is`
@@ -40,20 +40,21 @@
     @get:Rule
     var helper: MigrationTestHelper = MigrationTestHelper(
         InstrumentationRegistry.getInstrumentation(),
-        MigrationDbKotlin::class.java.canonicalName,
-        FrameworkSQLiteOpenHelperFactory()
+        MigrationDbKotlin::class.java
     )
 
     companion object {
         val TEST_DB = "migration-test"
     }
 
+    abstract class EmptyDb : RoomDatabase()
+
     @Test
     @Throws(IOException::class)
     fun giveBadResource() {
         val helper = MigrationTestHelper(
             InstrumentationRegistry.getInstrumentation(),
-            "foo", FrameworkSQLiteOpenHelperFactory()
+            EmptyDb::class.java
         )
         try {
             helper.createDatabase(TEST_DB, 1)
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
index fa6fa9d..3206ff9 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
@@ -16,17 +16,13 @@
 
 package androidx.room.integration.testapp.migration;
 
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.fail;
-
-import android.database.sqlite.SQLiteException;
 
 import androidx.annotation.NonNull;
-import androidx.room.Room;
 import androidx.room.migration.Migration;
 import androidx.room.testing.MigrationTestHelper;
+import androidx.room.util.TableInfo;
 import androidx.sqlite.db.SupportSQLiteDatabase;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
@@ -49,88 +45,42 @@
     public MigrationTestHelper helper;
 
     public AutoMigrationTest() {
-        helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
-                AutoMigrationDb.class.getCanonicalName());
+        helper = new MigrationTestHelper(
+                InstrumentationRegistry.getInstrumentation(),
+                AutoMigrationDb.class
+        );
     }
 
     // Run this to create the very 1st version of the db.
     public void createFirstVersion() throws IOException {
-        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 2);
+        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
+        db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
         db.close();
     }
 
-    /**
-     * Tests the case where a non existent auto migration is called.
-     */
-    @Test
-    public void testBadAutoMigrationInput() throws IOException {
-        try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1)) {
-            db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
-            AutoMigrationDb autoMigrationDbV2 = getLatestDb();
-            helper.runMigrationsAndValidate(
-                    TEST_DB,
-                    2,
-                    true,
-                    autoMigrationDbV2.getAutoGeneratedMigration(3, 4)
-            );
-            fail();
-        } catch (IllegalArgumentException ex) {
-            assertThat(
-                    ex.getMessage(),
-                    is("No AutoMigrations between versions 'from = 3', 'to = "
-                    + "4' have been provided. Annotate Database class with @AutoMigration(from = "
-                    + "3, to = 4) to generate this AutoMigration.")
-            );
-        }
-    }
-
     @Test
     public void goFromV1ToV2() throws IOException {
-        try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1)) {
-            db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
-        }
-        AutoMigrationDb autoMigrationDbV2 = getLatestDb();
-        helper.runMigrationsAndValidate(
+        createFirstVersion();
+        SupportSQLiteDatabase db = helper.runMigrationsAndValidate(
                 TEST_DB,
                 2,
                 true
         );
-        assertThat(autoMigrationDbV2.dao().getAllEntity1s().size(), is(1));
+        final TableInfo info = TableInfo.read(db, AutoMigrationDb.Entity1.TABLE_NAME);
+        assertThat(info.columns.size(), is(3));
     }
 
     /**
      * Verifies that the user defined migration is selected over using an autoMigration.
      */
     @Test
-    public void goFromV1ToV2WithUserDefinedMigration() throws IOException {
-        try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1)) {
-            db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
-        }
-
-        try {
-            AutoMigrationDb autoMigrationDbV2 = Room.databaseBuilder(
-                    InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                    AutoMigrationDb.class, TEST_DB).addMigrations(MIGRATION_1_2).build();
-            autoMigrationDbV2.getOpenHelper().getWritableDatabase(); // trigger open
-            helper.closeWhenFinished(autoMigrationDbV2);
-
-            helper.runMigrationsAndValidate(
-                    TEST_DB,
-                    2,
-                    true
-            );
-        } catch (SQLiteException exception) {
-            assertThat(exception.getMessage(), containsString("no such table: Entity0"));
-        }
-    }
-
-    private AutoMigrationDb getLatestDb() {
-        AutoMigrationDb db = Room.databaseBuilder(
-                InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                AutoMigrationDb.class, TEST_DB).build();
-        db.getOpenHelper().getWritableDatabase(); // trigger open
-        helper.closeWhenFinished(db);
-        return db;
+    public void testAutoMigrationsNotProcessedBeforeCustomMigrations() throws IOException {
+        helper.runMigrationsAndValidate(
+                TEST_DB,
+                2,
+                true,
+                MIGRATION_1_2
+        );
     }
 
     private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java
index da5c158..6fdb83b 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java
@@ -27,11 +27,11 @@
 import androidx.room.Entity;
 import androidx.room.PrimaryKey;
 import androidx.room.ProvidedAutoMigrationSpec;
-import androidx.room.Room;
 import androidx.room.RoomDatabase;
 import androidx.room.migration.AutoMigrationSpec;
 import androidx.room.testing.MigrationTestHelper;
 import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -41,6 +41,8 @@
 import org.junit.runner.RunWith;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Test custom database migrations.
@@ -53,11 +55,24 @@
             new ProvidedAutoMigrationDb.MyProvidedAutoMigration("Hi");
 
     @Rule
-    public MigrationTestHelper helper;
+    public MigrationTestHelper helperWithoutSpec;
+    public MigrationTestHelper helperWithSpec;
 
     public ProvidedAutoMigrationSpecTest() {
-        helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
-                ProvidedAutoMigrationDb.class.getCanonicalName());
+        helperWithoutSpec = new MigrationTestHelper(
+                InstrumentationRegistry.getInstrumentation(),
+                ProvidedAutoMigrationDb.class
+        );
+
+        List<AutoMigrationSpec> specs = new ArrayList<>();
+        specs.add(mProvidedSpec);
+
+        helperWithSpec = new MigrationTestHelper(
+                InstrumentationRegistry.getInstrumentation(),
+                ProvidedAutoMigrationDb.class,
+                specs,
+                new FrameworkSQLiteOpenHelperFactory()
+        );
     }
 
     @Database(
@@ -123,15 +138,15 @@
 
     // Run this to create the very 1st version of the db.
     public void createFirstVersion() throws IOException {
-        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 2);
+        SupportSQLiteDatabase db = helperWithoutSpec.createDatabase(TEST_DB, 1);
+        db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
         db.close();
     }
 
     @Test
     public void testOnPostMigrate() throws IOException {
-        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
-        ProvidedAutoMigrationDb autoMigrationDbV2 = getLatestDb();
-        helper.runMigrationsAndValidate(
+        createFirstVersion();
+        helperWithSpec.runMigrationsAndValidate(
                 TEST_DB,
                 2,
                 true
@@ -143,32 +158,24 @@
      * Verifies that the user defined migration is selected over using an autoMigration.
      */
     @Test
-    public void testNoSpecProvidedInConfig() {
+    public void testNoSpecProvidedInConfig() throws IOException {
+        createFirstVersion();
         try {
-            ProvidedAutoMigrationDb autoMigrationDbV2 = Room.databaseBuilder(
-                    InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                    ProvidedAutoMigrationDb.class, TEST_DB).build();
+            helperWithoutSpec.runMigrationsAndValidate(
+                    TEST_DB,
+                    2,
+                    true
+            );
         } catch (IllegalArgumentException exception) {
             assertThat(
                     exception.getMessage(),
                     containsString(
                             "A required auto migration spec (androidx.room.integration.testapp"
                                     + ".migration.ProvidedAutoMigrationSpecTest"
-                                    + ".ProvidedAutoMigrationDb.MyProvidedAutoMigration) is "
-                                    + "missing in the database configuration."
+                                    + ".ProvidedAutoMigrationDb.MyProvidedAutoMigration) has not "
+                                    + "been provided."
                     )
             );
         }
     }
-
-    private ProvidedAutoMigrationDb getLatestDb() {
-        ProvidedAutoMigrationDb db = Room.databaseBuilder(
-                InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                ProvidedAutoMigrationDb.class, TEST_DB)
-                .addAutoMigrationSpec(mProvidedSpec)
-                .build();
-        db.getOpenHelper().getWritableDatabase(); // trigger open
-        helper.closeWhenFinished(db);
-        return db;
-    }
 }
diff --git a/room/room-paging/api/current.txt b/room/room-paging/api/current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/room/room-paging/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/room/room-paging/api/public_plus_experimental_current.txt b/room/room-paging/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/room/room-paging/api/public_plus_experimental_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/room/room-paging/api/res-current.txt b/room/room-paging/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/room/room-paging/api/res-current.txt
diff --git a/room/room-paging/api/restricted_current.txt b/room/room-paging/api/restricted_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/room/room-paging/api/restricted_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/room/room-paging/build.gradle b/room/room-paging/build.gradle
new file mode 100644
index 0000000..e9645b8
--- /dev/null
+++ b/room/room-paging/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(KOTLIN_STDLIB)
+    // Add dependencies here
+}
+
+androidx {
+    name = "Room Paging"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenGroup = LibraryGroups.ROOM
+    inceptionYear = "2021"
+    description = "Room Paging integration"
+    publish = Publish.NONE
+}
diff --git a/room/room-paging/src/androidTest/AndroidManifest.xml b/room/room-paging/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..50ba2eb
--- /dev/null
+++ b/room/room-paging/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.room.paging.test">
+
+</manifest>
diff --git a/room/room-paging/src/main/AndroidManifest.xml b/room/room-paging/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fa18cd8
--- /dev/null
+++ b/room/room-paging/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.room.paging">
+
+</manifest>
\ No newline at end of file
diff --git a/room/runtime/api/current.txt b/room/runtime/api/current.txt
index 50f212f..c6616ad 100644
--- a/room/runtime/api/current.txt
+++ b/room/runtime/api/current.txt
@@ -52,7 +52,6 @@
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
     method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
     method @Deprecated public void endTransaction();
-    method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
     method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
     method public java.util.concurrent.Executor getQueryExecutor();
diff --git a/room/runtime/api/public_plus_experimental_current.txt b/room/runtime/api/public_plus_experimental_current.txt
index 758a9f6..bc1f3c0 100644
--- a/room/runtime/api/public_plus_experimental_current.txt
+++ b/room/runtime/api/public_plus_experimental_current.txt
@@ -42,6 +42,7 @@
   public class Room {
     ctor @Deprecated public Room();
     method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> databaseBuilder(android.content.Context, Class<T!>, String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <T, C> T getGeneratedImplementation(Class<C!>, String);
     method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> inMemoryDatabaseBuilder(android.content.Context, Class<T!>);
     field public static final String MASTER_TABLE_NAME = "room_master_table";
   }
@@ -55,7 +56,6 @@
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
     method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
     method @Deprecated public void endTransaction();
-    method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
     method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
     method public java.util.concurrent.Executor getQueryExecutor();
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index 14c301a..51e2dac 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -94,7 +94,6 @@
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
     method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
     method @Deprecated public void endTransaction();
-    method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
     method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
     method public java.util.concurrent.Executor getQueryExecutor();
diff --git a/room/runtime/src/main/java/androidx/room/Room.java b/room/runtime/src/main/java/androidx/room/Room.java
index 6a570c4..d252099 100644
--- a/room/runtime/src/main/java/androidx/room/Room.java
+++ b/room/runtime/src/main/java/androidx/room/Room.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 
 /**
  * Utility class for Room.
@@ -75,7 +76,9 @@
 
     @SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
     @NonNull
-    static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static <T, C> T getGeneratedImplementation(@NonNull Class<C> klass,
+            @NonNull String suffix) {
         final String fullPackage = klass.getPackage().getName();
         String name = klass.getCanonicalName();
         final String postPackageName = fullPackage.isEmpty()
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index 3c79716..d15c9c2 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -227,7 +227,7 @@
             }
         }
 
-        List<Migration> autoMigrations = getAutoMigrations();
+        List<Migration> autoMigrations = getAutoMigrations(mAutoMigrationSpecs);
         for (Migration autoMigration : autoMigrations) {
             boolean migrationExists = configuration.migrationContainer.getMigrations()
                             .containsKey(autoMigration.startVersion);
@@ -311,43 +311,16 @@
      * Returns a list of {@link Migration} of a database that have been automatically generated.
      *
      * @return A list of migration instances each of which is a generated autoMigration
-     */
-    @NonNull
-    protected List<Migration> getAutoMigrations() {
-        return Collections.emptyList();
-    }
-
-    /**
-     * Returns a {@link Migration} of a database that have been generated using
-     * {@link AutoMigration} with the specific "from" and "to" version pair.
-     * <p>
-     * If a {@link Migration} with the given "from" and "to" versions cannot be found, this
-     * method will throw a {@link IllegalArgumentException}.
-     *
-     * <p>
-     * This API is intended for testing and all auto-migrations are added by default.
-     *
-     * @param from version of the original database schema to migrate from
-     * @param to version of the new database schema to migrate to
-     * @return migration instance of a generated autoMigration
+     * @param autoMigrationSpecs
      *
      * @hide
      */
     @NonNull
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public Migration getAutoGeneratedMigration(int from, int to) {
-        // TODO: (b/181985265) Support testing automigrations in MigrationTestHelper and remove
-        //  this method
-        List<Migration> autoMigrations = getAutoMigrations();
-        for (Migration autoMigration : autoMigrations) {
-            if (autoMigration.startVersion == from && autoMigration.endVersion == to) {
-                return autoMigration;
-            }
-        }
-        throw new IllegalArgumentException("No AutoMigrations between versions 'from = " + from
-                + "', 'to = " + to + "' have been provided. Annotate Database class with "
-                + "@AutoMigration(from = " + from + ", to = " + to + ") to generate this "
-                + "AutoMigration.");
+    public List<Migration> getAutoMigrations(
+            @NonNull Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> autoMigrationSpecs
+    ) {
+        return Collections.emptyList();
     }
 
     /**
@@ -427,7 +400,7 @@
      */
     @NonNull
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    protected Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
+    public Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
         return Collections.emptySet();
     }
 
diff --git a/room/testing/api/current.txt b/room/testing/api/current.txt
index 891c1b7..ea0639e 100644
--- a/room/testing/api/current.txt
+++ b/room/testing/api/current.txt
@@ -2,8 +2,11 @@
 package androidx.room.testing {
 
   public class MigrationTestHelper extends org.junit.rules.TestWatcher {
-    ctor public MigrationTestHelper(android.app.Instrumentation!, String!);
-    ctor public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+    ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!);
+    ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory);
     method public void closeWhenFinished(androidx.sqlite.db.SupportSQLiteDatabase!);
     method public void closeWhenFinished(androidx.room.RoomDatabase!);
     method public androidx.sqlite.db.SupportSQLiteDatabase! createDatabase(String!, int) throws java.io.IOException;
diff --git a/room/testing/api/public_plus_experimental_current.txt b/room/testing/api/public_plus_experimental_current.txt
index 891c1b7..ea0639e 100644
--- a/room/testing/api/public_plus_experimental_current.txt
+++ b/room/testing/api/public_plus_experimental_current.txt
@@ -2,8 +2,11 @@
 package androidx.room.testing {
 
   public class MigrationTestHelper extends org.junit.rules.TestWatcher {
-    ctor public MigrationTestHelper(android.app.Instrumentation!, String!);
-    ctor public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+    ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!);
+    ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory);
     method public void closeWhenFinished(androidx.sqlite.db.SupportSQLiteDatabase!);
     method public void closeWhenFinished(androidx.room.RoomDatabase!);
     method public androidx.sqlite.db.SupportSQLiteDatabase! createDatabase(String!, int) throws java.io.IOException;
diff --git a/room/testing/api/restricted_current.txt b/room/testing/api/restricted_current.txt
index 891c1b7..ea0639e 100644
--- a/room/testing/api/restricted_current.txt
+++ b/room/testing/api/restricted_current.txt
@@ -2,8 +2,11 @@
 package androidx.room.testing {
 
   public class MigrationTestHelper extends org.junit.rules.TestWatcher {
-    ctor public MigrationTestHelper(android.app.Instrumentation!, String!);
-    ctor public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+    ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!);
+    ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>);
+    ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory);
     method public void closeWhenFinished(androidx.sqlite.db.SupportSQLiteDatabase!);
     method public void closeWhenFinished(androidx.room.RoomDatabase!);
     method public androidx.sqlite.db.SupportSQLiteDatabase! createDatabase(String!, int) throws java.io.IOException;
diff --git a/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java b/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
index c846037..02c5192 100644
--- a/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
+++ b/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
@@ -23,12 +23,15 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.arch.core.executor.ArchTaskExecutor;
+import androidx.room.AutoMigration;
 import androidx.room.DatabaseConfiguration;
 import androidx.room.Room;
 import androidx.room.RoomDatabase;
 import androidx.room.RoomOpenHelper;
 import androidx.room.RoomOpenHelper.ValidationResult;
+import androidx.room.migration.AutoMigrationSpec;
 import androidx.room.migration.Migration;
 import androidx.room.migration.bundle.DatabaseBundle;
 import androidx.room.migration.bundle.DatabaseViewBundle;
@@ -91,14 +94,27 @@
     private List<WeakReference<RoomDatabase>> mManagedRoomDatabases = new ArrayList<>();
     private boolean mTestStarted;
     private Instrumentation mInstrumentation;
+    @Nullable
+    private List<AutoMigrationSpec> mSpecs;
+    @Nullable
+    private Class<? extends RoomDatabase> mDatabaseClass;
 
     /**
      * Creates a new migration helper. It uses the Instrumentation context to load the schema
      * (falls back to the app resources) and the target context to create the database.
      *
+     * @deprecated Cannot be used to run migration tests involving {@link AutoMigration}.
+     * <p>
+     * To test {@link AutoMigration}, you must use
+     * {@link #MigrationTestHelper(Instrumentation, Class, List, SupportSQLiteOpenHelper.Factory)}
+     * for tests containing a {@link androidx.room.ProvidedAutoMigrationSpec}, or use
+     * {@link #MigrationTestHelper(Instrumentation, Class, List)}
+     * otherwise.
+     *
      * @param instrumentation The instrumentation instance.
      * @param assetsFolder    The asset folder in the assets directory.
      */
+    @Deprecated
     public MigrationTestHelper(Instrumentation instrumentation, String assetsFolder) {
         this(instrumentation, assetsFolder, new FrameworkSQLiteOpenHelperFactory());
     }
@@ -107,18 +123,88 @@
      * Creates a new migration helper. It uses the Instrumentation context to load the schema
      * (falls back to the app resources) and the target context to create the database.
      *
+     * @deprecated Cannot be used to run migration tests involving {@link AutoMigration}.
+     * <p>
+     * To test {@link AutoMigration}, you must use
+     * {@link #MigrationTestHelper(Instrumentation, Class, List, SupportSQLiteOpenHelper.Factory)}
+     * for tests containing a {@link androidx.room.ProvidedAutoMigrationSpec}, or use
+     * {@link #MigrationTestHelper(Instrumentation, Class, List)}
+     * otherwise.
+     *
      * @param instrumentation The instrumentation instance.
      * @param assetsFolder    The asset folder in the assets directory.
      * @param openFactory     Factory class that allows creation of {@link SupportSQLiteOpenHelper}
      */
+    @Deprecated
     public MigrationTestHelper(Instrumentation instrumentation, String assetsFolder,
             SupportSQLiteOpenHelper.Factory openFactory) {
         mInstrumentation = instrumentation;
+        mAssetsFolder = assetsFolder;
+        mOpenFactory = openFactory;
+        mDatabaseClass = null;
+        mSpecs = new ArrayList<>();
+    }
+
+    /**
+     * Creates a new migration helper. It uses the Instrumentation context to load the schema
+     * (falls back to the app resources) and the target context to create the database.
+     *
+     * @param instrumentation The instrumentation instance.
+     * @param databaseClass   The Database class to be tested.
+     */
+    public MigrationTestHelper(@NonNull Instrumentation instrumentation,
+            @NonNull Class<? extends RoomDatabase> databaseClass) {
+        this(instrumentation, databaseClass, new ArrayList<>(),
+                new FrameworkSQLiteOpenHelperFactory());
+    }
+
+    /**
+     * Creates a new migration helper. It uses the Instrumentation context to load the schema
+     * (falls back to the app resources) and the target context to create the database.
+     * <p>
+     * An instance of a class annotated with {@link androidx.room.ProvidedAutoMigrationSpec} has
+     * to be provided to Room using this constructor. MigrationTestHelper will map auto migration
+     * spec classes to their provided instances before running and validatingt the Migrations.
+     *
+     * @param instrumentation The instrumentation instance.
+     * @param databaseClass   The Database class to be tested.
+     * @param specs           The list of available auto migration specs that will be provided to
+     *                        Room at runtime.
+     */
+    public MigrationTestHelper(@NonNull Instrumentation instrumentation,
+            @NonNull Class<? extends RoomDatabase> databaseClass,
+            @NonNull List<AutoMigrationSpec> specs) {
+        this(instrumentation, databaseClass, specs, new FrameworkSQLiteOpenHelperFactory());
+    }
+
+    /**
+     * Creates a new migration helper. It uses the Instrumentation context to load the schema
+     * (falls back to the app resources) and the target context to create the database.
+     * <p>
+     * An instance of a class annotated with {@link androidx.room.ProvidedAutoMigrationSpec} has
+     * to be provided to Room using this constructor. MigrationTestHelper will map auto migration
+     * spec classes to their provided instances before running and validatingt the Migrations.
+     *
+     * @param instrumentation The instrumentation instance.
+     * @param databaseClass   The Database class to be tested.
+     * @param specs           The list of available auto migration specs that will be provided to
+     *                        Room at runtime.
+     * @param openFactory     Factory class that allows creation of {@link SupportSQLiteOpenHelper}
+     */
+    public MigrationTestHelper(@NonNull Instrumentation instrumentation,
+            @NonNull Class<? extends RoomDatabase> databaseClass,
+            @NonNull List<AutoMigrationSpec> specs,
+            @NonNull SupportSQLiteOpenHelper.Factory openFactory
+    ) {
+        String assetsFolder = databaseClass.getCanonicalName();
+        mInstrumentation = instrumentation;
         if (assetsFolder.endsWith("/")) {
             assetsFolder = assetsFolder.substring(0, assetsFolder.length() - 1);
         }
         mAssetsFolder = assetsFolder;
         mOpenFactory = openFactory;
+        mDatabaseClass = databaseClass;
+        mSpecs = specs;
     }
 
     @Override
@@ -144,7 +230,7 @@
         if (dbPath.exists()) {
             Log.d(TAG, "deleting database file " + name);
             if (!dbPath.delete()) {
-                throw new IllegalStateException("there is a database file and i could not delete"
+                throw new IllegalStateException("There is a database file and I could not delete"
                         + " it. Make sure you don't have any open connections to that database"
                         + " before calling this method.");
             }
@@ -214,6 +300,7 @@
         }
         SchemaBundle schemaBundle = loadSchema(version);
         RoomDatabase.MigrationContainer container = new RoomDatabase.MigrationContainer();
+        container.addMigrations(getAutoMigrations(mSpecs));
         container.addMigrations(migrations);
         DatabaseConfiguration configuration = new DatabaseConfiguration(
                 mInstrumentation.getTargetContext(),
@@ -244,6 +331,75 @@
         return openDatabase(name, roomOpenHelper);
     }
 
+    /**
+     * Returns a list of {@link Migration} of a database that has been generated using
+     * {@link AutoMigration}.
+     */
+    @NonNull
+    private List<Migration> getAutoMigrations(List<AutoMigrationSpec> userProvidedSpecs) {
+        if (mDatabaseClass == null) {
+            if (userProvidedSpecs.isEmpty()) {
+                // TODO: Detect that there are auto migrations to test when a deprecated
+                //  constructor is used.
+                Log.e(TAG, "If you have any AutoMigrations in your implementation, you must use "
+                        + "a non-deprecated MigrationTestHelper constructor to provide the "
+                        + "Database class in order to test them. If you do not have any "
+                        + "AutoMigrations to test, you may ignore this warning.");
+                return new ArrayList<>();
+            } else {
+                throw new IllegalStateException("You must provide the database class in the "
+                        + "MigrationTestHelper constructor in order to test auto migrations.");
+            }
+        }
+
+        RoomDatabase db = Room.getGeneratedImplementation(mDatabaseClass, "_Impl");
+        Set<Class<? extends AutoMigrationSpec>> requiredAutoMigrationSpecs =
+                db.getRequiredAutoMigrationSpecs();
+        return db.getAutoMigrations(
+                createAutoMigrationSpecMap(requiredAutoMigrationSpecs, userProvidedSpecs)
+        );
+    }
+
+    /**
+     * Maps auto migration spec classes to their provided instance.
+     */
+    private Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> createAutoMigrationSpecMap(
+            Set<Class<? extends AutoMigrationSpec>> requiredAutoMigrationSpecs,
+            List<AutoMigrationSpec> userProvidedSpecs) {
+        Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> specMap = new HashMap<>();
+        if (requiredAutoMigrationSpecs.isEmpty()) {
+            return specMap;
+        }
+
+        if (userProvidedSpecs == null) {
+            throw new IllegalStateException(
+                    "You must provide all required auto migration specs in the "
+                            + "MigrationTestHelper constructor."
+            );
+        }
+
+        for (Class<? extends AutoMigrationSpec> spec : requiredAutoMigrationSpecs) {
+            boolean found = false;
+            AutoMigrationSpec match = null;
+            for (AutoMigrationSpec provided : userProvidedSpecs) {
+                if (spec.isAssignableFrom(provided.getClass())) {
+                    found = true;
+                    match = provided;
+                    break;
+                }
+            }
+            if (!found) {
+                throw new IllegalArgumentException(
+                        "A required auto migration spec (" + spec.getCanonicalName() + ") has not"
+                                + " been provided."
+                );
+            }
+            specMap.put(spec, match);
+        }
+        return specMap;
+    }
+
+
     private SupportSQLiteDatabase openDatabase(String name, RoomOpenHelper roomOpenHelper) {
         SupportSQLiteOpenHelper.Configuration config =
                 SupportSQLiteOpenHelper.Configuration
diff --git a/security/security-app-authenticator-testing/OWNERS b/security/security-app-authenticator-testing/OWNERS
new file mode 100644
index 0000000..00fc159
--- /dev/null
+++ b/security/security-app-authenticator-testing/OWNERS
@@ -0,0 +1 @@
+mpgroover@google.com
diff --git a/security/security-app-authenticator-testing/api/current.txt b/security/security-app-authenticator-testing/api/current.txt
new file mode 100644
index 0000000..9e8fd21
--- /dev/null
+++ b/security/security-app-authenticator-testing/api/current.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.security.app.authenticator {
+
+  public final class TestAppAuthenticatorBuilder {
+    method public androidx.security.app.authenticator.AppAuthenticator build() throws androidx.security.app.authenticator.AppAuthenticatorXmlException, java.io.IOException;
+    method public static androidx.security.app.authenticator.TestAppAuthenticatorBuilder createFromInputStream(android.content.Context, java.io.InputStream) throws androidx.security.app.authenticator.AppAuthenticatorXmlException;
+    method public static androidx.security.app.authenticator.TestAppAuthenticatorBuilder createFromResource(android.content.Context, @XmlRes int);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setPackageNotInstalled(String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setSignatureAcceptedForPackage(String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setSigningIdentityForPackage(String, String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setTestPolicy(int);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setUidForPackage(String, int);
+    field public static final int POLICY_CUSTOM = 3; // 0x3
+    field public static final int POLICY_DENY_ALL = 2; // 0x2
+    field public static final int POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES = 1; // 0x1
+  }
+
+}
+
diff --git a/security/security-app-authenticator-testing/api/public_plus_experimental_current.txt b/security/security-app-authenticator-testing/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..9e8fd21
--- /dev/null
+++ b/security/security-app-authenticator-testing/api/public_plus_experimental_current.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.security.app.authenticator {
+
+  public final class TestAppAuthenticatorBuilder {
+    method public androidx.security.app.authenticator.AppAuthenticator build() throws androidx.security.app.authenticator.AppAuthenticatorXmlException, java.io.IOException;
+    method public static androidx.security.app.authenticator.TestAppAuthenticatorBuilder createFromInputStream(android.content.Context, java.io.InputStream) throws androidx.security.app.authenticator.AppAuthenticatorXmlException;
+    method public static androidx.security.app.authenticator.TestAppAuthenticatorBuilder createFromResource(android.content.Context, @XmlRes int);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setPackageNotInstalled(String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setSignatureAcceptedForPackage(String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setSigningIdentityForPackage(String, String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setTestPolicy(int);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setUidForPackage(String, int);
+    field public static final int POLICY_CUSTOM = 3; // 0x3
+    field public static final int POLICY_DENY_ALL = 2; // 0x2
+    field public static final int POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES = 1; // 0x1
+  }
+
+}
+
diff --git a/security/security-app-authenticator-testing/api/res-current.txt b/security/security-app-authenticator-testing/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/security/security-app-authenticator-testing/api/res-current.txt
diff --git a/security/security-app-authenticator-testing/api/restricted_current.txt b/security/security-app-authenticator-testing/api/restricted_current.txt
new file mode 100644
index 0000000..9e8fd21
--- /dev/null
+++ b/security/security-app-authenticator-testing/api/restricted_current.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.security.app.authenticator {
+
+  public final class TestAppAuthenticatorBuilder {
+    method public androidx.security.app.authenticator.AppAuthenticator build() throws androidx.security.app.authenticator.AppAuthenticatorXmlException, java.io.IOException;
+    method public static androidx.security.app.authenticator.TestAppAuthenticatorBuilder createFromInputStream(android.content.Context, java.io.InputStream) throws androidx.security.app.authenticator.AppAuthenticatorXmlException;
+    method public static androidx.security.app.authenticator.TestAppAuthenticatorBuilder createFromResource(android.content.Context, @XmlRes int);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setPackageNotInstalled(String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setSignatureAcceptedForPackage(String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setSigningIdentityForPackage(String, String);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setTestPolicy(int);
+    method public androidx.security.app.authenticator.TestAppAuthenticatorBuilder setUidForPackage(String, int);
+    field public static final int POLICY_CUSTOM = 3; // 0x3
+    field public static final int POLICY_DENY_ALL = 2; // 0x2
+    field public static final int POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES = 1; // 0x1
+  }
+
+}
+
diff --git a/security/security-app-authenticator-testing/build.gradle b/security/security-app-authenticator-testing/build.gradle
new file mode 100644
index 0000000..5d92e64
--- /dev/null
+++ b/security/security-app-authenticator-testing/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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.LibraryType
+import androidx.build.Publish
+
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_CORE
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_EXT_JUNIT
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_RULES
+import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_RUNNER
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+dependencies {
+    implementation (project(":security:security-app-authenticator"))
+    implementation("androidx.collection:collection:1.1.0")
+
+    androidTestImplementation("junit:junit:4.13")
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+}
+
+android {
+    testOptions.unitTests.includeAndroidResources = true
+}
+
+androidx {
+    name = "Android Security App Package Authenticator Testing"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.SECURITY_APP_AUTHENTICATOR_TESTING
+    mavenGroup = LibraryGroups.SECURITY
+    inceptionYear = "2021"
+    description = "This library provides a configurable AppAuthenticator that can be used during testing"
+}
diff --git a/security/security-app-authenticator-testing/src/androidTest/AndroidManifest.xml b/security/security-app-authenticator-testing/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..901dec6
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.security.app.authenticator.testing.test">
+
+</manifest>
+
diff --git a/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilderTest.java b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilderTest.java
new file mode 100644
index 0000000..83d27b6
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilderTest.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2021 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.security.app.authenticator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.security.app.authenticator.testing.test.R;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class TestAppAuthenticatorBuilderTest {
+    private static final String DECLARED_PACKAGE1 = "com.android.app1";
+    private static final String DECLARED_PACKAGE2 = "com.android.app2";
+    private static final String UNDECLARED_PACKAGE = "com.android.undeclared.app";
+    private static final String EXPECTED_IDENTITY_PACKAGE = "com.social.app";
+    private static final String TEST_PERMISSION =
+            "androidx.security.app.authenticator.TEST_PERMISSION";
+
+    private Context mContext;
+    private Resources mResources;
+    private TestAppAuthenticatorBuilder mBuilderFromResource;
+    private TestAppAuthenticatorBuilder mBuilderFromInputStream;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = ApplicationProvider.getApplicationContext();
+        mResources = mContext.getResources();
+        mBuilderFromResource = TestAppAuthenticatorBuilder.createFromResource(mContext,
+                R.xml.test_config);
+        mBuilderFromInputStream = TestAppAuthenticatorBuilder.createFromInputStream(mContext,
+                mResources.openRawResource(R.raw.test_config));
+    }
+
+    @Test
+    public void verifyAppIdentity_defaultPolicyDeclaredPackage_returnsMatch() throws Exception {
+        // By default the TestAppAuthenticator returns a test instance that will report all declared
+        // packages have the expected signing identity. This test verifies this default behavior
+        // using the declared test packages from the config.
+        AppAuthenticator appAuthenticatorFromResource = mBuilderFromResource.build();
+        AppAuthenticator appAuthenticatorFromInputStream = mBuilderFromInputStream.build();
+
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.SIGNATURE_MATCH,
+                appAuthenticatorFromResource.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertEquals(AppAuthenticator.SIGNATURE_MATCH,
+                appAuthenticatorFromInputStream.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        // Since the AppAuthenticator with this policy should return permission granted the
+        // enforce version of this method should not throw any exceptions.
+        appAuthenticatorFromResource.enforceCallingAppIdentity(
+                DECLARED_PACKAGE1, TEST_PERMISSION);
+        appAuthenticatorFromInputStream.enforceCallingAppIdentity(
+                DECLARED_PACKAGE1, TEST_PERMISSION);
+        appAuthenticatorFromResource.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE);
+        appAuthenticatorFromInputStream.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE);
+    }
+
+    @Test
+    public void verifyAppIdentity_defaultPolicyUndeclaredPackage_returnsNoMatch() throws Exception {
+        // This test verifies the default policy used by the instance returned from the
+        // TestAppAuthenticator will report a package as not having an expected signing identity
+        // if it is not declared in the XML config.
+        AppAuthenticator appAuthenticatorFromResource = mBuilderFromResource.build();
+        AppAuthenticator appAuthenticatorFromInputStream = mBuilderFromInputStream.build();
+
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_NO_MATCH,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        UNDECLARED_PACKAGE, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_NO_MATCH,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        UNDECLARED_PACKAGE, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.SIGNATURE_NO_MATCH,
+                appAuthenticatorFromResource.checkAppIdentity(UNDECLARED_PACKAGE));
+        assertEquals(AppAuthenticator.SIGNATURE_NO_MATCH,
+                appAuthenticatorFromInputStream.checkAppIdentity(UNDECLARED_PACKAGE));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceCallingAppIdentity(
+                        UNDECLARED_PACKAGE, TEST_PERMISSION));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceCallingAppIdentity(
+                        UNDECLARED_PACKAGE, TEST_PERMISSION));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceAppIdentity(UNDECLARED_PACKAGE));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceAppIdentity(UNDECLARED_PACKAGE));
+
+
+    }
+
+    @Test
+    public void verifyAppIdentity_denyAllPolicyDeclaredPackage_returnsNoMatch() throws Exception {
+        // The TestAppAuthenticator also provides an option to specify a deny all policy that will
+        // report any package does not have the expected signing identity even if it is declared
+        // in the XML config.
+        AppAuthenticator appAuthenticatorFromResource =
+                mBuilderFromResource.setTestPolicy(
+                        TestAppAuthenticatorBuilder.POLICY_DENY_ALL).build();
+        AppAuthenticator appAuthenticatorFromInputStream =
+                mBuilderFromInputStream.setTestPolicy(
+                        TestAppAuthenticatorBuilder.POLICY_DENY_ALL).build();
+
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_NO_MATCH,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_NO_MATCH,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.SIGNATURE_NO_MATCH,
+                appAuthenticatorFromResource.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertEquals(AppAuthenticator.SIGNATURE_NO_MATCH,
+                appAuthenticatorFromInputStream.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+    }
+
+    @Test
+    public void verifyAppIdentity_declaredPackageWithExpectedSigningIdentity_returnsMatch()
+            throws Exception {
+        // The TestAppAuthenticator provides an option to specify the signing identity of a package;
+        // this test verifies when the specified signing identity matches that in the provided XML
+        // config the instance returned from TestAppAuthenticator reports the match.
+        AppAuthenticator appAuthenticatorFromResource =
+                mBuilderFromResource.setSigningIdentityForPackage(DECLARED_PACKAGE1,
+                        "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8")
+                        .setSigningIdentityForPackage(EXPECTED_IDENTITY_PACKAGE,
+                        "d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469")
+                        .build();
+        AppAuthenticator appAuthenticatorFromInputStream =
+                mBuilderFromInputStream.setSigningIdentityForPackage(DECLARED_PACKAGE1,
+                        "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8")
+                        .setSigningIdentityForPackage(EXPECTED_IDENTITY_PACKAGE,
+                        "d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469")
+                        .build();
+
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.SIGNATURE_MATCH,
+                appAuthenticatorFromResource.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertEquals(AppAuthenticator.SIGNATURE_MATCH,
+                appAuthenticatorFromInputStream.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        appAuthenticatorFromResource.enforceCallingAppIdentity(
+                DECLARED_PACKAGE1, TEST_PERMISSION);
+        appAuthenticatorFromResource.enforceCallingAppIdentity(
+                DECLARED_PACKAGE1, TEST_PERMISSION);
+        appAuthenticatorFromResource.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE);
+        appAuthenticatorFromInputStream.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE);
+    }
+
+    @Test
+    public void callingAppIdentity_undeclaredPackageWithExpectedSigningIdentity_returnsMatch()
+            throws Exception {
+        // By using the setSigningIdentityForPackage method a test can set the signing identity
+        // for a package that is not explicitly declared in the XML config; this can be useful
+        // for configs that make use of the all-packages tag and thus cannot use the default
+        // "accept all declared packages" policy.
+        // Note, the expected-identity tag does not support an all-packages declaration, so only
+        // the calling identity is verified here.
+        AppAuthenticator appAuthenticatorFromResource =
+                mBuilderFromResource.setSigningIdentityForPackage(UNDECLARED_PACKAGE,
+                        "681b0e56a796350c08647352a4db800cc44b2adc8f4c72fa350bd05d4d50264d")
+                        .build();
+        AppAuthenticator appAuthenticatorFromInputStream =
+                mBuilderFromInputStream.setSigningIdentityForPackage(UNDECLARED_PACKAGE,
+                        "681b0e56a796350c08647352a4db800cc44b2adc8f4c72fa350bd05d4d50264d")
+                        .build();
+
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        UNDECLARED_PACKAGE, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        UNDECLARED_PACKAGE, TEST_PERMISSION));
+        appAuthenticatorFromResource.enforceCallingAppIdentity(UNDECLARED_PACKAGE, TEST_PERMISSION);
+        appAuthenticatorFromInputStream.enforceCallingAppIdentity(
+                UNDECLARED_PACKAGE, TEST_PERMISSION);
+    }
+
+    @Test
+    public void callingAppIdentity_packageUidMismatch_returnsUidMismatch() throws Exception {
+        // The uid of the calling app can be set through the TestAppAuthenticator to test scenarios
+        // when the ID of the calling app does not match that of the specified package. This test
+        // verifies the AppAuthenticator instance returned from the TestAppAuthenticator returns
+        // the proper result for this mismatch.
+        final int packageUid = 10001;
+        final int callingUid = 10123;
+        final int callingPid = 1234;
+        AppAuthenticator appAuthenticatorFromResource =
+                mBuilderFromResource.setUidForPackage(DECLARED_PACKAGE1, packageUid).build();
+        AppAuthenticator appAuthenticatorFromInputStream =
+                mBuilderFromInputStream.setUidForPackage(DECLARED_PACKAGE1, packageUid).build();
+
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_PACKAGE_UID_MISMATCH,
+                appAuthenticatorFromResource.checkCallingAppIdentity(DECLARED_PACKAGE1,
+                        TEST_PERMISSION, callingPid, callingUid));
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_PACKAGE_UID_MISMATCH,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(DECLARED_PACKAGE1,
+                        TEST_PERMISSION, callingPid, callingUid));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceCallingAppIdentity(DECLARED_PACKAGE1,
+                        TEST_PERMISSION, callingPid, callingUid));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceCallingAppIdentity(DECLARED_PACKAGE1,
+                        TEST_PERMISSION, callingPid, callingUid));
+    }
+
+    @Test
+    public void verifyAppIdentity_signatureAcceptedForPackage_returnsExpectedResult()
+            throws Exception {
+        // The TestAppAuthenticator allows packages to be individually set to accept the signing
+        // identity. This test verifies the signature is accepted for specified packages, but is
+        // rejected for all other packages.
+        AppAuthenticator appAuthenticatorFromResource =
+                mBuilderFromResource.setSignatureAcceptedForPackage(
+                        DECLARED_PACKAGE1).setSignatureAcceptedForPackage(
+                        EXPECTED_IDENTITY_PACKAGE).build();
+        AppAuthenticator appAuthenticatorFromInputStream =
+                mBuilderFromInputStream.setSignatureAcceptedForPackage(
+                        DECLARED_PACKAGE1).setSignatureAcceptedForPackage(
+                        EXPECTED_IDENTITY_PACKAGE).build();
+
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromResource.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertEquals(AppAuthenticator.PERMISSION_GRANTED,
+                appAuthenticatorFromInputStream.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        // A package declared in the XML config but not set explicitly to be accepted should be
+        // rejected.
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_NO_MATCH,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        DECLARED_PACKAGE2, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_NO_MATCH,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        DECLARED_PACKAGE2, TEST_PERMISSION));
+        appAuthenticatorFromResource.enforceCallingAppIdentity(DECLARED_PACKAGE1, TEST_PERMISSION);
+        appAuthenticatorFromInputStream.enforceCallingAppIdentity(
+                DECLARED_PACKAGE1, TEST_PERMISSION);
+        appAuthenticatorFromResource.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE);
+        appAuthenticatorFromInputStream.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE);
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceCallingAppIdentity(
+                        DECLARED_PACKAGE2, TEST_PERMISSION));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceCallingAppIdentity(
+                        DECLARED_PACKAGE2, TEST_PERMISSION));
+    }
+
+    @Test
+    public void callingAppIdentity_packageNotInstalled_returnsUnknownPackage() throws Exception {
+        // The TestAppAuthenticator can be configured to treat a package as uninstalled to verify
+        // scenarios where the package being queried is not available on the device.
+        AppAuthenticator appAuthenticatorFromResource =
+                mBuilderFromResource.setPackageNotInstalled(
+                        DECLARED_PACKAGE1).setPackageNotInstalled(
+                        EXPECTED_IDENTITY_PACKAGE).build();
+        AppAuthenticator appAuthenticatorFromInputStream =
+                mBuilderFromInputStream.setPackageNotInstalled(
+                        DECLARED_PACKAGE1).setPackageNotInstalled(
+                        EXPECTED_IDENTITY_PACKAGE).build();
+
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_UNKNOWN_PACKAGE,
+                appAuthenticatorFromResource.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.PERMISSION_DENIED_UNKNOWN_PACKAGE,
+                appAuthenticatorFromInputStream.checkCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertEquals(AppAuthenticator.SIGNATURE_NO_MATCH,
+                appAuthenticatorFromResource.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertEquals(AppAuthenticator.SIGNATURE_NO_MATCH,
+                appAuthenticatorFromInputStream.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceCallingAppIdentity(
+                        DECLARED_PACKAGE1, TEST_PERMISSION));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromResource.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+        assertThrows(SecurityException.class, () ->
+                appAuthenticatorFromInputStream.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+    }
+
+    @Test
+    public void setSigningIdentityForPackage_digestCaseMismatch_returnsMatch() throws Exception {
+        // The TestAppAuthenticatorBuilder supports specifying a signing identity for a package
+        // under test; while the AppAuthenticator will normalize the digest in the config file to
+        // match the case output by the AppAuthenticatorUtils#computeDigest, the signing identity
+        // provided to the TestAppAuthenticatorBuilder#setSigningIdentityForPackage can be
+        // specified in either case. This test ensures regardless of the case provided to this
+        // method the value is normalized and a match can be successfully verified.
+        AppAuthenticator appAuthenticator =
+                mBuilderFromResource.setSigningIdentityForPackage(EXPECTED_IDENTITY_PACKAGE,
+                        "6A8B96E278E58F62CFE3584022CEC1D0527FCB85A9E5D2E1694EB0405BE5B599")
+                        .build();
+
+        assertEquals(AppAuthenticator.SIGNATURE_MATCH,
+                appAuthenticator.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+    }
+}
diff --git a/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorUtilsTest.java b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorUtilsTest.java
new file mode 100644
index 0000000..ccee60b
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorUtilsTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021 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.security.app.authenticator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class TestAppAuthenticatorUtilsTest {
+    private static final String TEST_PACKAGE = "com.android.app1";
+
+    private TestAppAuthenticatorUtils.Builder mBuilder;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = ApplicationProvider.getApplicationContext();
+        mBuilder = new TestAppAuthenticatorUtils.Builder(context);
+    }
+
+    @Test
+    public void getUidForPackage_defaultConfig_returnsBinderCallingUid() throws Exception {
+        // By default the TestAppAuthenticatorUtils should return Binder#getCallingUid as the UID
+        // of any specified package.
+        TestAppAuthenticatorUtils utils = mBuilder.build();
+
+        assertEquals(Binder.getCallingUid(), utils.getUidForPackage(TEST_PACKAGE));
+    }
+
+    @Test
+    public void getUidForPackage_setUidForPackage_returnsSetUid() throws Exception {
+        // The TestAppAuthentictorUtils allows the UID of a package to be explicit set to verify
+        // cases where the UID of the specified package does not match the UID of the calling
+        // package.
+        TestAppAuthenticatorUtils utils = mBuilder.setUidForPackage(TEST_PACKAGE, 1234).build();
+
+        assertEquals(1234, utils.getUidForPackage(TEST_PACKAGE));
+
+    }
+
+    @Test
+    public void getUidForPackage_packageNotInstalled_throwsException() throws Exception {
+        // The TestAppAuthenticatorUtils can be configured to treat a package as not installed;
+        // this will result in a PackageManager.NameNotFoundException being thrown, similar to
+        // what is thrown by the platform when invoking PackageManager#getPackageInfo with a
+        // package that is not installed on the device.
+        TestAppAuthenticatorUtils utils = mBuilder.setPackageNotInstalled(TEST_PACKAGE).build();
+
+        assertThrows(PackageManager.NameNotFoundException.class,
+                () -> utils.getUidForPackage(TEST_PACKAGE));
+    }
+}
diff --git a/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppSignatureVerifierTest.java b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppSignatureVerifierTest.java
new file mode 100644
index 0000000..ec5ea8db
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppSignatureVerifierTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2021 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.security.app.authenticator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import androidx.security.app.authenticator.testing.test.R;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class TestAppSignatureVerifierTest {
+    private static final String DECLARED_PACKAGE = "com.android.app1";
+    private static final String UNDECLARED_PACKAGE = "com.android.undeclared.app";
+    private static final String QUERY_TYPE = "test";
+    private static final String TEST_PERMISSION =
+            "androidx.security.app.authenticator.TEST_PERMISSION";
+
+    private TestAppSignatureVerifier.Builder mBuilder;
+    private Set<String> mPackageCertDigests;
+    private Set<String> mAllPackagesCertDigests;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = ApplicationProvider.getApplicationContext();
+        mBuilder = new TestAppSignatureVerifier.Builder(context);
+
+        XmlPullParser parser = context.getResources().getXml(R.xml.test_config);
+        AppAuthenticator.AppAuthenticatorConfig config =
+                AppAuthenticator.createConfigFromParser(parser);
+        Map<String, Map<String, Set<String>>> permissionAllowMap = config.getPermissionAllowMap();
+        mBuilder.setPermissionAllowMap(permissionAllowMap);
+        mBuilder.setExpectedIdentities(config.getExpectedIdentities());
+        // Set the test policy to custom since a majority of these test will use a custom config,
+        // and those that test a policy will set it explicitly.
+        mBuilder.setTestPolicy(TestAppAuthenticatorBuilder.POLICY_CUSTOM);
+        mPackageCertDigests = permissionAllowMap.get(TEST_PERMISSION).get(DECLARED_PACKAGE);
+        mAllPackagesCertDigests =
+                permissionAllowMap.get(TEST_PERMISSION).get(AppAuthenticator.ALL_PACKAGES_TAG);
+    }
+
+    @Test
+    public void verifySigningIdentityForQuery_policyAcceptDeclared_returnsExpectedResult()
+            throws Exception {
+        // The TestAppSignatureVerifier supports specifying a test policy; this test verifies the
+        // policy that accepts all declared packages does properly accept a package declared in
+        // the XML config and does not accept one that could potentially pass through an
+        // all-packages declaration.
+        TestAppSignatureVerifier verifier =
+                mBuilder.setTestPolicy(
+                        TestAppAuthenticatorBuilder.POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES)
+                        .build();
+
+        assertTrue(verifier.verifySigningIdentityForQuery(DECLARED_PACKAGE, QUERY_TYPE,
+                mPackageCertDigests, mAllPackagesCertDigests));
+        assertFalse(verifier.verifySigningIdentityForQuery(UNDECLARED_PACKAGE, QUERY_TYPE, null,
+                mAllPackagesCertDigests));
+    }
+
+    @Test
+    public void verifySigningIdentityForQuery_policyDenyAll_returnsNoMatch() throws Exception {
+        // The POLICY_DENY_ALL should cause all queries to be denied, even for packages
+        // explicitly declared in the app-authenticator's XML configuration.
+        TestAppSignatureVerifier verifier =
+                mBuilder.setTestPolicy(TestAppAuthenticatorBuilder.POLICY_DENY_ALL).build();
+
+        assertFalse(verifier.verifySigningIdentityForQuery(DECLARED_PACKAGE, QUERY_TYPE,
+                mPackageCertDigests, mAllPackagesCertDigests));
+        assertFalse(verifier.verifySigningIdentityForQuery(UNDECLARED_PACKAGE, QUERY_TYPE, null,
+                mAllPackagesCertDigests));
+    }
+
+    @Test
+    public void verifySigningIdentityForQuery_packageNotInstalled_returnsNoMatch()
+            throws Exception {
+        // The TestAppSignatureVerifier can be configured to treat apps as not installed; since
+        // they do not have a signing identity on the device this should cause the verifier to
+        // return no match for the exp signing identity.
+        TestAppSignatureVerifier verifier =
+                mBuilder.setPackageNotInstalled(DECLARED_PACKAGE).build();
+
+        assertFalse(verifier.verifySigningIdentityForQuery(DECLARED_PACKAGE, QUERY_TYPE,
+                mPackageCertDigests, mAllPackagesCertDigests));
+    }
+
+    @Test
+    public void verifySigningIdentityForQuery_packageSignatureAccepted_returnsMatch()
+            throws Exception {
+        // The TestAppSignatureVerifier can be configured to treat an app's signing identity as
+        // accepted.
+        TestAppSignatureVerifier verifier =
+                mBuilder.setSignatureAcceptedForPackage(DECLARED_PACKAGE).build();
+
+        assertTrue(verifier.verifySigningIdentityForQuery(DECLARED_PACKAGE, QUERY_TYPE,
+                mPackageCertDigests, mAllPackagesCertDigests));
+    }
+
+    @Test
+    public void verifySigningIdentityForQuery_packageSignatureSet_returnsExpectedValue()
+            throws Exception {
+        // The TestAppSignatureVerifier supports setting an explicit signing identity for a package;
+        // this can be used to test both that the configured identity matches the expected
+        // identity from the config file, but can also be used as a test case to verify a signing
+        // identity that is no longer trusted is not added back to the config file.
+        TestAppSignatureVerifier verifier =
+                mBuilder.setSigningIdentityForPackage(DECLARED_PACKAGE,
+                        "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8")
+                        .build();
+        // Use new Sets of package certs that do not match the configured digest above; note that
+        // the all-packages set is required as well since the config does have the same
+        // certificate under that element too.
+        Set<String> newPackageCertDigests = Set.of(
+                "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2");
+        Set<String> newAllPackagesCertDigests = Set.of(
+                "7d6fd7774f0d87624da6dcf16d0d3d104c3191e771fbe2f39c86aed4b2bf1a0f");
+
+        assertTrue(verifier.verifySigningIdentityForQuery(DECLARED_PACKAGE, QUERY_TYPE,
+                mPackageCertDigests, mAllPackagesCertDigests));
+        assertFalse(verifier.verifySigningIdentityForQuery(DECLARED_PACKAGE, QUERY_TYPE,
+                newPackageCertDigests, newAllPackagesCertDigests));
+    }
+}
diff --git a/security/security-app-authenticator-testing/src/androidTest/res/raw/test_config.xml b/security/security-app-authenticator-testing/src/androidTest/res/raw/test_config.xml
new file mode 100644
index 0000000..345e777
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/androidTest/res/raw/test_config.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2020 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.
+  -->
+<app-authenticator>
+    <expected-identity>
+        <package name="com.bank.app">
+            <cert-digest> fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8
+            </cert-digest>
+        </package>
+        <package name="com.social.app">
+            <cert-digest>6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599
+            </cert-digest>
+            <cert-digest>d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469
+            </cert-digest>
+        </package>
+    </expected-identity>
+    <permission name="androidx.security.app.authenticator.TEST_PERMISSION">
+        <all-packages>
+            <cert-digest>fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8
+            </cert-digest>
+            <cert-digest>681b0e56a796350c08647352a4db800cc44b2adc8f4c72fa350bd05d4d50264d
+            </cert-digest>
+        </all-packages>
+        <package name="com.android.app1">
+            <cert-digest>Fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8
+            </cert-digest>
+        </package>
+        <package name="com.android.app2">
+            <cert-digest>6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599
+            </cert-digest>
+            <cert-digest>d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469
+            </cert-digest>
+        </package>
+    </permission>
+</app-authenticator>
diff --git a/security/security-app-authenticator-testing/src/androidTest/res/xml/test_config.xml b/security/security-app-authenticator-testing/src/androidTest/res/xml/test_config.xml
new file mode 100644
index 0000000..f8e6cce
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/androidTest/res/xml/test_config.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2020 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.
+  -->
+<app-authenticator>
+    <expected-identity>
+        <package name="com.bank.app">
+            <cert-digest> fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8
+            </cert-digest>
+        </package>
+        <package name="com.social.app">
+            <cert-digest>6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599
+            </cert-digest>
+            <cert-digest>d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469
+            </cert-digest>
+        </package>
+    </expected-identity>
+    <permission name="androidx.security.app.authenticator.TEST_PERMISSION">
+        <all-packages>
+            <cert-digest>fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8
+            </cert-digest>
+            <cert-digest>681b0e56a796350c08647352a4db800cc44b2adc8f4c72fa350bd05d4d50264d
+            </cert-digest>
+        </all-packages>
+        <package name="com.android.app1">
+            <cert-digest>fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8
+            </cert-digest>
+        </package>
+        <package name="com.android.app2">
+            <cert-digest>6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599
+            </cert-digest>
+            <cert-digest>d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469
+            </cert-digest>
+        </package>
+    </permission>
+</app-authenticator>
diff --git a/security/security-app-authenticator-testing/src/main/AndroidManifest.xml b/security/security-app-authenticator-testing/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..24789e6
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.security.app.authenticator.testing">
+
+</manifest>
diff --git a/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilder.java b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilder.java
new file mode 100644
index 0000000..372a166
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilder.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2021 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.security.app.authenticator;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Binder;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.XmlRes;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Builder class that can be used to facilitate the creation of a new {@link AppAuthenticator} which
+ * can be configured to meet the requirements of the test. Similar to the {@code AppAuthenticator},
+ * the static factory methods for this class require either an XML resource or {@link InputStream}
+ * containing the {@code app-authenticator} configuration allowing verification of your declared
+ * config as part of the test.
+ *
+ * <p>There are several options to configure the behavior of the resulting {@code AppAuthenticator}.
+ * <ul>
+ *     <li>{@link #setTestPolicy(int)} - This sets a generic test policy. {@link
+ *     #POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES} will cause the {@code AppAuthenticator}
+ *     to always return that a queried package has the expected signing identity as long as it is
+ *     explicitly declared in your configuration; that is, the package must be declared in a
+ *     {@code package} element within either an {@code expected-identity} or {@code permission}
+ *     element. {@link #POLICY_DENY_ALL} will cause the {@code AppAuthenticator} to always return
+ *     that a queried package does not have the expected signing identity regardless of its
+ *     declaration. These two policies can be used to verify good path and error path for
+ *     scenarios where the package names can be explicitly declared in the XML configuration.
+ *     <p>{@code POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES} is the default policy when no
+ *     other options are configured. When any of the other set methods (except for {@link
+ *     #setUidForPackage(String, int)}) are invoked they will set the policy to {@link
+ *     #POLICY_CUSTOM}.
+ *     </li>
+ *     <li>{@link #setSignatureAcceptedForPackage(String)} - This configures the {@code
+ *     AppAuthenticator} to always return that the specified package has the expected signing
+ *     identity. Note this still requires the {@code app-authenticator} have a path to verify
+ *     the provided package; that is, the package must either be explicitly declared in a
+ *     {@code package} element or fall under a {@code all-packages} element for the query being
+ *     performed. This is to ensure that a package being verified during the test could also be
+ *     successfully verified in production for the given query.
+ *     </li>
+ *     <li>{@link #setSigningIdentityForPackage(String, String)} - This sets an explicit
+ *     signing identity for the provided package; the signing identity should be
+ *     specified as the SHA-256 digest of the DER encoding of the signing certificate, similar
+ *     to how digests are specified in the {@code app-authenticator} configuration file. While
+ *     this can be used to set a signing identity to the expected value, this is more often
+ *     used to set the signing identity to a value that should not be accepted. For instance, a
+ *     test suite could have a test that verifies a key that is no longer trusted is never
+ *     added back to the configuration file.
+ *     </li>
+ *     <li>{@link #setPackageNotInstalled(String)} - This configures the {@code AppAuthenticator}
+ *     to treat the specified package as not installed on the device. Since a package that is not
+ *     installed can result in a different return code from the {@code AppAuthenticator} methods
+ *     this configuration can be used to verify an app's behavior when an expected app is not
+ *     installed on the device.
+ *     </li>
+ *     <li>{@link #setUidForPackage(String, int)} - The {@code AppAuthenticator} will
+ *     always verify the UID of the calling package matches the specified UID (or
+ *     {@link Binder#getCallingUid()} if a UID is not specified). By default this test {@code
+ *     AppAuthenticator} will use the result of {@code Binder#getCallingUid()} as the UID of all
+ *     queried packages. This method can be used to verify the expected behavior when a calling
+ *     package's UID does not match the expected UID.
+ *     </li>
+ * </ul>
+ */
+// The purpose of this class is to build a configurable AppAuthenticator for tests so the builder
+// is the top level class.
+@SuppressLint("TopLevelBuilder")
+public final class TestAppAuthenticatorBuilder {
+    private Context mContext;
+    private XmlPullParser mParser;
+    private @TestPolicy int mTestPolicy;
+    private TestAppSignatureVerifier.Builder mAppSignatureVerifierBuilder;
+    private TestAppAuthenticatorUtils.Builder mAppAuthenticatorUtilsBuilder;
+
+    /**
+     * Private constructor that should only be called by the static factory methods.
+     *
+     * @param context the context within which to create the {@link AppAuthenticator}
+     * @param parser  an {@link XmlPullParser} containing the definitions for the
+     *                permissions and expected identities based on package / expected signing
+     *                certificate digests
+     */
+    private TestAppAuthenticatorBuilder(Context context, XmlPullParser parser) {
+        mContext = context;
+        mParser = parser;
+        mTestPolicy = POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES;
+        mAppSignatureVerifierBuilder = new TestAppSignatureVerifier.Builder(context);
+        mAppAuthenticatorUtilsBuilder = new TestAppAuthenticatorUtils.Builder(mContext);
+    }
+
+    /**
+     * This test policy will cause the AppAuthenticator to return a successful signing identity for
+     * all packages explicitly declared in the XML configuration. This is the default policy used
+     * when a new {@code AppAuthenticator} is built without calling {@link
+     * #setSigningIdentityForPackage(String, String)}, {@link
+     * #setSignatureAcceptedForPackage(String)}, and {@link #setPackageNotInstalled(String)}.
+     */
+    public static final int POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES = 1;
+    /**
+     * This test policy will cause the AppAuthenticator to return that the signing identity of
+     * the package does that match the expect identity from the XML configuration for all queried
+     * packages.
+     */
+    public static final int POLICY_DENY_ALL = 2;
+    /**
+     * This test policy indicates that the caller will specify the expected results for each
+     * package individually. This is the default policy used when a new {@code TestAppAuthenticator}
+     * is built after calling any of the following:
+     * {@link #setSigningIdentityForPackage(String, String)}, {@link
+     * #setSignatureAcceptedForPackage(String)}, and {@link #setPackageNotInstalled(String)}.
+     * Once the policy has been set to this value it cannot be changed to any of the other policies.
+     */
+    public static final int POLICY_CUSTOM = 3;
+
+    @IntDef(value = {
+            POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES,
+            POLICY_DENY_ALL,
+            POLICY_CUSTOM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface TestPolicy {
+    }
+
+    /**
+     * Returns a new {@link TestAppAuthenticatorBuilder} that can be used to create a new {@link
+     * AppAuthenticator} configured to behave as required for the test.
+     *
+     * @param context     the context within which to create the {@link AppAuthenticator}
+     * @param xmlResource the ID of the XML resource containing the definitions for the
+     *                    permissions and expected identities based on package / expected signing
+     *                    certificate digests
+     * @return this instance of the {@code TestAppAuthenticatorBuilder}
+     */
+    // This is not a setter for the builder but instead a static factory method to obtain a new
+    // builder.
+    @SuppressLint("BuilderSetStyle")
+    @NonNull
+    public static TestAppAuthenticatorBuilder createFromResource(@NonNull Context context,
+            @XmlRes int xmlResource) {
+        Resources resources = context.getResources();
+        XmlPullParser parser = resources.getXml(xmlResource);
+        return new TestAppAuthenticatorBuilder(context, parser);
+    }
+
+    /**
+     * Returns a new {@link TestAppAuthenticatorBuilder} that can be used to create a new {@link
+     * AppAuthenticator} configured to behave as required for the test.
+     *
+     * @param context        the context within which to create the {@link AppAuthenticator}
+     * @param xmlInputStream the XML {@link InputStream} containing the definitions for the
+     *                       permissions and expected identities based on packages / expected
+     *                       signing certificate digests
+     * @return this instance of the {@code TestAppAuthenticatorBuilder}
+     */
+    // This is not a setter for the builder but instead a static factory method to obtain a new
+    // builder.
+    @SuppressLint("BuilderSetStyle")
+    @NonNull
+    public static TestAppAuthenticatorBuilder createFromInputStream(
+            @NonNull Context context,
+            @NonNull InputStream xmlInputStream)
+            throws AppAuthenticatorXmlException {
+        XmlPullParser parser;
+        try {
+            parser = XmlPullParserFactory.newInstance().newPullParser();
+            parser.setInput(xmlInputStream, null);
+        } catch (XmlPullParserException e) {
+            throw new AppAuthenticatorXmlException("Unable to create parser from provided "
+                    + "InputStream", e);
+        }
+        return new TestAppAuthenticatorBuilder(context, parser);
+    }
+
+    /**
+     * Sets the policy to be used by the {@link AppAuthenticator} for the test.
+     *
+     * @param testPolicy the test policy to be used by the {@code AppAuthenticator{}
+     * @return this instance of the {@code TestAppAuthenticatorBuilder}
+     * @see #POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES
+     * @see #POLICY_DENY_ALL
+     * @see #POLICY_CUSTOM
+     */
+    // The builder allows configuring other options that are not directly controlled by the
+    // AppAuthenticator.
+    @SuppressLint("MissingGetterMatchingBuilder")
+    public @NonNull TestAppAuthenticatorBuilder setTestPolicy(@TestPolicy int testPolicy) {
+        mTestPolicy = testPolicy;
+        return this;
+    }
+
+    /**
+     * Configures the resulting {@link AppAuthenticator} to always return that the signing
+     * identity matches the expected value when the specified {@code packageName} is queried.
+     *
+     * <p>Note, the specified {@code packageName} must be defined either explicitly via a
+     * {@code package} element or implicitly via a {@code all-packages} element; this ensures
+     * that the XML configuration is correct and that the specified package could be verified
+     * on device.
+     *
+     * @param packageName the name of the package for which the signing identity should be
+     *                    treated as matching the expected value
+     * @return this instance of the {@code TestAppAuthenticatorBuilder}
+     */
+    // The builder allows configuring other options that are not directly controlled by the
+    // AppAuthenticator.
+    @SuppressLint("MissingGetterMatchingBuilder")
+    @NonNull
+    public TestAppAuthenticatorBuilder setSignatureAcceptedForPackage(
+            @NonNull String packageName) {
+        mTestPolicy = POLICY_CUSTOM;
+        mAppSignatureVerifierBuilder.setSignatureAcceptedForPackage(packageName);
+        return this;
+    }
+
+    /**
+     * Sets the provided {@code certDigest} as the signing identity for the specified {@code
+     * packageName}.
+     *
+     * @param packageName the name of the package that will use the provided signing identity
+     * @param certDigest  the digest to be treated as the signing identity of the specified package
+     * @return this instance of the {@code TestAppAuthenticatorBuilder}
+     */
+    // The builder allows configuring other options that are not directly controlled by the
+    // AppAuthenticator.
+    @SuppressLint("MissingGetterMatchingBuilder")
+    @NonNull
+    public TestAppAuthenticatorBuilder setSigningIdentityForPackage(
+            @NonNull String packageName,
+            @NonNull String certDigest) {
+        mTestPolicy = POLICY_CUSTOM;
+        mAppSignatureVerifierBuilder.setSigningIdentityForPackage(packageName,
+                AppAuthenticator.normalizeCertDigest(certDigest))
+        ;
+        return this;
+    }
+
+    /**
+     * Sets the provided {@code uid} as the UID of the specified {@code packageName}.
+     *
+     * <p>This method can be used to verify the scenario where a calling package does not have the
+     * expected calling UID.
+     *
+     * @param packageName the name of the package that will be treated as having the provided uid
+     * @param uid         the uid to use for the specified package
+     * @return this instance of the {@code TestAppAuthenticatorBuilder}
+     */
+    // The builder allows configuring other options that are not directly controlled by the
+    // AppAuthenticator.
+    @SuppressLint("MissingGetterMatchingBuilder")
+    @NonNull
+    public TestAppAuthenticatorBuilder setUidForPackage(@NonNull String packageName,
+            int uid) {
+        mAppAuthenticatorUtilsBuilder.setUidForPackage(packageName, uid);
+        return this;
+    }
+
+    /**
+     * Treats the provided {@code packageName} as not being installed by the resulting {@link
+     * AppAuthenticator}.
+     *
+     * @param packageName the name of the package to be treated as not installed
+     * @return this instance of the {@code TestAppAuthenticatorBuilder}
+     */
+    // The builder allows configuring other options that are not directly controlled by the
+    // AppAuthenticator.
+    @SuppressLint("MissingGetterMatchingBuilder")
+    @NonNull
+    public TestAppAuthenticatorBuilder setPackageNotInstalled(
+            @NonNull String packageName) {
+        mTestPolicy = POLICY_CUSTOM;
+        mAppAuthenticatorUtilsBuilder.setPackageNotInstalled(packageName);
+        mAppSignatureVerifierBuilder.setPackageNotInstalled(packageName);
+        return this;
+    }
+
+    /**
+     * Builds an {@link AppAuthenticator} with the specified config that can be injected to satisfy
+     * test requirements.
+     *
+     * @return a new {@code AppAuthenticator} that will respond to queries as configured
+     * @throws AppAuthenticatorXmlException if the provided XML config file is not in the proper
+     *                                      format to create a new {@code AppAuthenticator}
+     * @throws IOException                  if an IO error is encountered when attempting to read
+     *                                      the XML config file
+     */
+    // This class is provided so that apps can inject a configurable AppAuthenticator for their
+    // tests, so it needs access to the restricted test APIs.
+    @SuppressLint("RestrictedApi")
+    @NonNull
+    public AppAuthenticator build() throws AppAuthenticatorXmlException, IOException {
+        // Obtain the config from the AppAuthenticator class to ensure that the provided XML is
+        // properly configured.
+        AppAuthenticator.AppAuthenticatorConfig config =
+                AppAuthenticator.createConfigFromParser(mParser);
+
+        // Configure the AppSignatureVerifier that will by the test AppAuthenticator.
+        mAppSignatureVerifierBuilder.setPermissionAllowMap(config.getPermissionAllowMap());
+        mAppSignatureVerifierBuilder.setExpectedIdentities(config.getExpectedIdentities());
+        mAppSignatureVerifierBuilder.setTestPolicy(mTestPolicy);
+
+        // Inject the AppSignatureVerifier and AppAuthenticatorUtils into the AppAuthenticator
+        // to configure it to behave as requested.
+        AppAuthenticator appAuthenticator = AppAuthenticator.createFromConfig(mContext, config);
+        appAuthenticator.setAppSignatureVerifier(mAppSignatureVerifierBuilder.build());
+        appAuthenticator.setAppAuthenticatorUtils(mAppAuthenticatorUtilsBuilder.build());
+        return appAuthenticator;
+    }
+}
diff --git a/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorUtils.java b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorUtils.java
new file mode 100644
index 0000000..984854e
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorUtils.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2021 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.security.app.authenticator;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.collection.ArrayMap;
+import androidx.collection.ArraySet;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An extension of the {@link AppAuthenticatorUtils} used by the {@link AppAuthenticator} that
+ * can be injected into the {@code AppAuthenticator} to configure it to behave as required by the
+ * test.
+ *
+ * <p>This test class supports changing the UID of a specified package and treating the package
+ * as not being installed to allowing testing of error path scenarios.
+ */
+class TestAppAuthenticatorUtils extends AppAuthenticatorUtils {
+    private Map<String, Integer> mPackageUids;
+    private Set<String> mNotInstalledPackages;
+
+    /**
+     * Constructor; instances should be configured through the {@link Builder}.
+     */
+    TestAppAuthenticatorUtils(Context context, Map<String, Integer> packageUids,
+            Set<String> exceptionPackages) {
+        super(context);
+        mPackageUids = packageUids;
+        mNotInstalledPackages = exceptionPackages;
+    }
+
+    /**
+     * Builder for a new {@link TestAppAuthenticatorUtils} that allows this test class to be
+     * configured as required for the test.
+     */
+    static class Builder {
+        private Context mContext;
+        private Map<String, Integer> mPackageUids;
+        private Set<String> mNotInstalledPackages;
+
+        /**
+         * Creates a new {@code Builder} with the specified {@link Context}.
+         *
+         * @param context the {@code Context} within which to create the new Builder
+         */
+        Builder(Context context) {
+            mContext = context;
+            mPackageUids = new ArrayMap<>();
+            mNotInstalledPackages = new ArraySet<>();
+        }
+
+        /**
+         * Sets the {@code uid} to be returned when the specified {@code packageName} is queried.
+         *
+         * @param packageName the name of the package to be configured
+         * @param uid         the uid to return for the specified package
+         * @return this instance of the {@code Builder}
+         */
+        Builder setUidForPackage(String packageName, int uid) {
+            mPackageUids.put(packageName, uid);
+            return this;
+        }
+
+        /**
+         * Treats the provided {@code packageName} as not being installed; this will result in a
+         * {@link PackageManager.NameNotFoundException} being thrown when this package is queried.
+         *
+         * @param packageName the name of the package to be treated as not installed
+         * @return this instance of the {@code Builder}
+         */
+        Builder setPackageNotInstalled(String packageName) {
+            mNotInstalledPackages.add(packageName);
+            return this;
+        }
+
+        /**
+         * Builds an extension of the {@link AppAuthenticatorUtils} that can be injected to satisfy
+         * test requirements.
+         *
+         * @return a new {@link TestAppAuthenticatorUtils} that will respond to queries as
+         * configured.
+         */
+        TestAppAuthenticatorUtils build() {
+            return new TestAppAuthenticatorUtils(mContext, mPackageUids, mNotInstalledPackages);
+        }
+    }
+
+    /**
+     * Returns the UID configured for the specified {@code packageName}, or the calling UID if
+     * the UID of the package has not been configured.
+     *
+     * @param packageName the name of the package to be queried
+     * @return the UID of the specified package
+     * @throws PackageManager.NameNotFoundException if this class has been configured to treat
+     *                                              the specified package as not installed
+     */
+    @Override
+    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+        if (mNotInstalledPackages.contains(packageName)) {
+            throw new PackageManager.NameNotFoundException("Test configured to throw exception "
+                    + "for package " + packageName);
+        }
+        if (mPackageUids.containsKey(packageName)) {
+            return mPackageUids.get(packageName);
+        }
+        return getCallingUid();
+    }
+}
diff --git a/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppSignatureVerifier.java b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppSignatureVerifier.java
new file mode 100644
index 0000000..f5f533f
--- /dev/null
+++ b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppSignatureVerifier.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2021 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.security.app.authenticator;
+
+import android.content.Context;
+
+import androidx.collection.ArrayMap;
+import androidx.collection.ArraySet;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An extension of the {@link AppSignatureVerifier} used by the {@link AppAuthenticator} that can
+ * be injected into the {@code AppAuthenticator} to configure it to behave as required by the test.
+ *
+ * <p>This test class supports setting a {@link TestAppAuthenticatorBuilder.TestPolicy},
+ * configuring generic acceptance per package, specifying the signing identity per package, and
+ * treating packages as not installed.
+ */
+class TestAppSignatureVerifier extends AppSignatureVerifier {
+    /**
+     * A Set of classes to be treated as always accepted as long as they are in the XML config file.
+     */
+    private final Set<String> mSignatureAcceptedPackages;
+    /**
+     * A Set of classes to be treated as not installed.
+     */
+    private final Set<String> mNotInstalledPackages;
+    /**
+     * A mapping from the package name to the digest to be used as the signing identity for the
+     * package during the test.
+     */
+    private final Map<String, String> mSigningIdentities;
+    /**
+     * The test policy to be used.
+     */
+    private final @TestAppAuthenticatorBuilder.TestPolicy int mTestPolicy;
+
+    /**
+     * Constructor that should only be invoked by the {@link Builder}.
+     */
+    TestAppSignatureVerifier(Context context,
+            Map<String, Map<String, Set<String>>> permissionAllowMap,
+            Map<String, Set<String>> expectedIdentities,
+            Set<String> signatureAcceptedPackages,
+            Set<String> notInstalledPackages,
+            Map<String, String> signingIdentities,
+            @TestAppAuthenticatorBuilder.TestPolicy int testPolicy) {
+        super(context, permissionAllowMap, expectedIdentities,
+                AppAuthenticator.DEFAULT_DIGEST_ALGORITHM, new NullCache());
+        mSignatureAcceptedPackages = signatureAcceptedPackages;
+        mNotInstalledPackages = notInstalledPackages;
+        mSigningIdentities = signingIdentities;
+        mTestPolicy = testPolicy;
+    }
+
+    /*
+     * Builder for a new {@link TestAppSignatureVerifier} that allows this test class to be
+     * configured as required for the test.
+     */
+    static class Builder {
+        private final Context mContext;
+        private Map<String, Map<String, Set<String>>> mPermissionAllowMap;
+        private Map<String, Set<String>> mExpectedIdentities;
+        private Set<String> mSignatureAcceptedPackages;
+        private Set<String> mNotInstalledPackages;
+        private String mDigestAlgorithm;
+        private Map<String, String> mSigningIdentities;
+        private @ TestAppAuthenticatorBuilder.TestPolicy int mTestPolicy;
+
+        /**
+         * Constructor accepting the {@code context} used to instantiate a new {@code
+         * TestAppSignatureVerifier}.
+         */
+        Builder(Context context) {
+            mContext = context;
+            mSignatureAcceptedPackages = new ArraySet<>();
+            mNotInstalledPackages = new ArraySet<>();
+            mSigningIdentities = new ArrayMap<>();
+        }
+
+        /**
+         * Configures the resulting {@link TestAppSignatureVerifier} to always return that the
+         * signing identity matches the expected value when the specified {@code packageName} is
+         * queried.
+         *
+         * @param packageName the name of the package for which the signing identity should be
+         *                    treated as matching the expected value
+         * @return this instance of the {@code Builder}
+         */
+        Builder setSignatureAcceptedForPackage(String packageName) {
+            mSignatureAcceptedPackages.add(packageName);
+            return this;
+        }
+
+        /**
+         * Sets the provided {@code certDigest} as the signing identity for the specified {@code
+         * packageName}.
+         *
+         * @param packageName the name of the package that will use the provided signing identity
+         * @param certDigest the digest to be treated as the signing identity of the specified
+         *                  package
+         * @return this instance of the {@code Builder}
+         */
+        Builder setSigningIdentityForPackage(String packageName, String certDigest) {
+            mSigningIdentities.put(packageName, certDigest);
+            return this;
+        }
+
+        /**
+         * Sets the {@code permissionAllowMap} to be used by the {@code TestAppSignatureVerifier}.
+         *
+         * This {@code Map} should contain a mapping from permission names to a mapping of package
+         * names to expected signing identities; each permission can also contain a mapping to
+         * the {@link AppAuthenticator#ALL_PACKAGES_TAG} which allow signing identities to be
+         * specified without knowing the exact packages that will be signed by them.
+         *
+         * @return this instance of the {@code Builder}
+         */
+        Builder setPermissionAllowMap(Map<String, Map<String, Set<String>>> permissionAllowMap) {
+            mPermissionAllowMap = permissionAllowMap;
+            return this;
+        }
+
+        /**
+         * Sets the {@code expectedIdentities} to be used by the {@code TestAppSignatureVerifier}.
+         *
+         * This {@code Map} should contain a mapping from package name to the expected signing
+         * certificate digest(s).
+         *
+         * @return this instance of the {@code Builder}
+         */
+        Builder setExpectedIdentities(Map<String, Set<String>> expectedIdentities) {
+            mExpectedIdentities = expectedIdentities;
+            return this;
+        }
+
+        /**
+         * Sets the test policy to be used by the {@code TestAppSignatureVerifier}.
+         *
+         * @return this instance of the {@code Builder}
+         */
+        Builder setTestPolicy(@ TestAppAuthenticatorBuilder.TestPolicy int testPolicy) {
+            mTestPolicy = testPolicy;
+            return this;
+        }
+
+        /**
+         * Treats the provided {@code packageName} as not being installed by the resulting {@link
+         * TestAppSignatureVerifier}.
+         *
+         * @param packageName the name of the package to be treated as not installed
+         * @return this instance of the {@code Builder}
+         */
+        Builder setPackageNotInstalled(String packageName) {
+            mNotInstalledPackages.add(packageName);
+            return this;
+        }
+
+        /**
+         * Builds a new {@code TestAppSignatureVerifier} instance using the provided configuration.
+         */
+        TestAppSignatureVerifier build() {
+            if (mPermissionAllowMap == null) {
+                mPermissionAllowMap = new ArrayMap<>();
+            }
+            if (mExpectedIdentities == null) {
+                mExpectedIdentities = new ArrayMap<>();
+            }
+            if (mDigestAlgorithm == null) {
+                mDigestAlgorithm = AppAuthenticator.DEFAULT_DIGEST_ALGORITHM;
+            }
+            return new TestAppSignatureVerifier(mContext, mPermissionAllowMap, mExpectedIdentities,
+                    mSignatureAcceptedPackages, mNotInstalledPackages, mSigningIdentities,
+                    mTestPolicy);
+        }
+    }
+
+    /**
+     * Responds to a signing identity query using the specified config for the provided {@code
+     * packageName} where the package is expected to have the signing identity in the {@code
+     * packageCertDigests} and, where applicable, {@code all-packages} are supported with the
+     * {@code allPackagesCertDigests}.
+     *
+     * <p>Package queries are performed in the following order:
+     * <ul>
+     *     <li>If the test policy is {@code POLICY_DENY_ALL} then {@code false} is returned</li>
+     *     {li>If the test policy is {@code POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES} then
+     *     {@code true} is returned as long as the specified package is explicitly declared with one
+     *     or more signing identities for this query</li>
+     *     <li>If the package is configured to be treated as not installed {@code false} is
+     *     returned</li>
+     *     <li>If the package is configured to have its signing identity accepted then {@code
+     *     true} is returned</li>
+     *     <li>If a signing identity is configured for the package then it is compared against
+     *     the expected signing identity declared in the XML config; if there is a match then
+     *     {@code true} is returned</li>
+     * </ul>
+     * @param packageName the name of the package being queried
+     * @param query the type of query being performed
+     * @param packageCertDigests a {@code Set} of certificate digests that are expected for the
+     *                           package
+     * @param allPackagesCertDigests a {@code Set} of certificate digests that are expected for
+     *                               any package for this query
+     * @return {@code true} if the package can be treated as successfully verified based on the
+     * test configuration
+     */
+    @Override
+    boolean verifySigningIdentityForQuery(String packageName, String query,
+            Set<String> packageCertDigests, Set<String> allPackagesCertDigests) {
+        if (mTestPolicy ==  TestAppAuthenticatorBuilder.POLICY_DENY_ALL) {
+            return false;
+        }
+        if (mTestPolicy
+                == TestAppAuthenticatorBuilder.POLICY_SIGNATURE_ACCEPTED_FOR_DECLARED_PACKAGES) {
+            // packageCertDigests will only be set if the package is explicitly declared for the
+            // query
+            return packageCertDigests != null;
+        }
+        if (mNotInstalledPackages.contains(packageName)) {
+            return false;
+        }
+        if (mSignatureAcceptedPackages.contains(packageName)) {
+            return true;
+        }
+        String certDigest = mSigningIdentities.get(packageName);
+        if (certDigest != null) {
+            if (packageCertDigests != null && packageCertDigests.contains(certDigest)) {
+                return true;
+            }
+            if (allPackagesCertDigests != null && allPackagesCertDigests.contains(certDigest)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * A test version of the {@code Cache} that always returns {@code null} for a cache query;
+     * this is intended to always force the test to go through the configured verification
+     * process as opposed to returning a previous query result.
+     */
+    static class NullCache extends Cache {
+        /**
+         * Instantiates a new NullCache; since it is not intended to return cached values a max
+         * size is not accepted, but a value of 1 is used since a value <= 0 is treated as an
+         * error by the {@link androidx.collection.LruCache}.
+         */
+        NullCache() {
+            super(1);
+        }
+
+        /**
+         * Overrides the {@link Cache#get} method to return a null value for all cache queries.
+         */
+        @Override
+        CacheEntry get(String packageName, String query) {
+            return null;
+        }
+    }
+}
diff --git a/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java b/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
index f7402d3..b22b902 100644
--- a/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
+++ b/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
@@ -322,4 +322,22 @@
                 "d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469"));
         assertEquals("SHA-256", config.getDigestAlgorithm());
     }
+
+    @Test
+    public void createConfigFromParser_upperCaseDigestInConfig_returnsMatch() throws Exception {
+        // The digest computed by the AppAuthenticatorUtils is in lower case, but the
+        // AppAuthenticator supports matching digests provided in upper case as well.
+        // This test does not directly verify the digest of a package's signing certificate
+        // but instead uses the bytes from the package name in the identity; this test ensures
+        // the AppAuthenticator properly normalizes the provided digest so that it matches the
+        // digest returned by AppAuthenticatorUtils.
+        final String packageName = "com.example.app";
+        AppAuthenticator.AppAuthenticatorConfig config = AppAuthenticator.createConfigFromParser(
+                mResources.getXml(R.xml.upper_case_digest));
+        Set<String> expectedPackageIdentities = config.getExpectedIdentities().get(packageName);
+
+        assertTrue(expectedPackageIdentities.contains(
+                AppAuthenticatorUtils.computeDigest(AppAuthenticator.DEFAULT_DIGEST_ALGORITHM,
+                        packageName.getBytes())));
+    }
 }
diff --git a/security/security-app-authenticator/src/androidTest/res/xml/upper_case_digest.xml b/security/security-app-authenticator/src/androidTest/res/xml/upper_case_digest.xml
new file mode 100644
index 0000000..179c964
--- /dev/null
+++ b/security/security-app-authenticator/src/androidTest/res/xml/upper_case_digest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 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.
+  -->
+<app-authenticator>
+    <expected-identity>
+        <package name="com.example.app">
+          <cert-digest>8A464E05BF037AF2432200C8687455FCD1E0A804D69C8A30D29DA59F584AC77F
+          </cert-digest>
+        </package>
+    </expected-identity>
+</app-authenticator>
diff --git a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java
index fcb1f2b..0e9f201 100644
--- a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java
+++ b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java
@@ -38,6 +38,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -145,7 +146,7 @@
      * @param appSignatureVerifier the verifier to be used to verify app signing identities
      * @param appAuthenticatorUtils the utils to be used
      */
-    private AppAuthenticator(AppSignatureVerifier appSignatureVerifier,
+    AppAuthenticator(AppSignatureVerifier appSignatureVerifier,
             AppAuthenticatorUtils appAuthenticatorUtils) {
         mAppSignatureVerifier = appSignatureVerifier;
         mAppAuthenticatorUtils = appAuthenticatorUtils;
@@ -413,6 +414,24 @@
     private static AppAuthenticator createFromParser(Context context, XmlPullParser parser)
             throws AppAuthenticatorXmlException, IOException {
         AppAuthenticatorConfig config = createConfigFromParser(parser);
+        return createFromConfig(context, config);
+    }
+
+    /**
+     * Creates a new {@code AppAuthenticator} that can be used to guard resources based on
+     * package name / signing identity as well as allow verification of expected signing identities
+     * before interacting with other apps on a device using the configuration defined in the
+     * provided {@code config}.
+     *
+     * @param context the context within which to create the {@code AppAuthenticator}
+     * @param config  an {@link AppAuthenticatorConfig} containing the definitions for the
+     *                permissions and expected identities based on package / expected signing
+     *                certificate digests
+     * @return a new {@code AppAuthenticator} that can be used to enforce the signing identities
+     * defined in the provided {@code config}
+     */
+    static AppAuthenticator createFromConfig(Context context,
+            @NonNull AppAuthenticatorConfig config) {
         AppSignatureVerifier verifier = AppSignatureVerifier.builder(context)
                 .setPermissionAllowMap(config.getPermissionAllowMap())
                 .setExpectedIdentities(config.getExpectedIdentities())
@@ -422,7 +441,7 @@
     }
 
     /**
-     * Creates a new {@code AppAuthentictorConfig} that can be used to instantiate a new {@code
+     * Creates a new {@code AppAuthenticatorConfig} that can be used to instantiate a new {@code
      * AppAuthenticator} with the specified config.
      *
      * @param parser an {@link XmlPullParser} containing the definition for the permissions and
@@ -573,13 +592,28 @@
                         + "on line " + parser.getLineNumber() + " must have non-empty text "
                         + "containing the certificate digest of the signer");
             }
-            allowedCertDigests.add(digest);
+            allowedCertDigests.add(normalizeCertDigest(digest));
             eventType = parser.nextTag();
         }
         return allowedCertDigests;
     }
 
     /**
+     * Normalizes the provided {@code certDigest} to ensure it is in the proper form for {@code
+     * Collection} membership checks when comparing a package's signing certificate digest against
+     * those provided to the {@code AppAuthenticator}.
+     *
+     * @param certDigest the digest to be normalized
+     * @return a normalized form of the provided digest that can be used in subsequent {@code
+     * Collection} membership checks
+     */
+    static String normalizeCertDigest(String certDigest) {
+        // The AppAuthenticatorUtils#computeDigest method uses lower case characters to compute the
+        // digest.
+        return certDigest.toLowerCase(Locale.US);
+    }
+
+    /**
      * Moves the provided {@code parser} to the next {@link XmlPullParser#START_TAG} or {@link
      * XmlPullParser#END_DOCUMENT} if the end of the document is reached, returning the value of
      * the event type.
diff --git a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java
index ac8a291..3df7674 100644
--- a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java
+++ b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java
@@ -210,7 +210,7 @@
      * The {@code query} can either be a permission or {@code EXPECTED_IDENTITY_QUERY} when
      * verifying the identity of another app before establishing communication.
      */
-    private boolean verifySigningIdentityForQuery(String packageName, String query,
+    boolean verifySigningIdentityForQuery(String packageName, String query,
             Set<String> packageCertDigests, Set<String> allPackagesCertDigests) {
         AppSigningInfo appSigningInfo;
         try {
diff --git a/settings.gradle b/settings.gradle
index efc92e7..ba10801 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -504,7 +504,9 @@
 includeProject(":preference:preference-ktx", "preference/preference-ktx", [BuildType.MAIN])
 includeProject(":print:print", "print/print", [BuildType.MAIN])
 includeProject(":profileinstaller:profileinstaller", "profileinstaller/profileinstaller", [BuildType.MAIN, BuildType.COMPOSE])
-includeProject(":profileinstaller:profileinstaller:integration-tests:testapp", "profileinstaller/profileinstaller/integration-tests/testapp", [BuildType.COMPOSE])
+includeProject(":profileinstaller:integration-tests:init-macrobenchmark", "profileinstaller/integration-tests/init-macrobenchmark", [BuildType.MAIN])
+includeProject(":profileinstaller:integration-tests:init-macrobenchmark-target", "profileinstaller/integration-tests/init-macrobenchmark-target", [BuildType.MAIN])
+includeProject(":profileinstaller:integration-tests:testapp", "profileinstaller/integration-tests/testapp", [BuildType.COMPOSE])
 includeProject(":recommendation:recommendation", "recommendation/recommendation", [BuildType.MAIN])
 includeProject(":recyclerview:recyclerview", "recyclerview/recyclerview", [BuildType.MAIN])
 includeProject(":recyclerview:recyclerview-benchmark", "recyclerview/recyclerview-benchmark", [BuildType.MAIN])
@@ -527,6 +529,7 @@
 includeProject(":room:room-guava", "room/guava", [BuildType.MAIN])
 includeProject(":room:room-ktx", "room/ktx", [BuildType.MAIN])
 includeProject(":room:room-migration", "room/migration", [BuildType.MAIN])
+includeProject(":room:room-paging", "room/room-paging", [BuildType.MAIN])
 includeProject(":room:room-runtime", "room/runtime", [BuildType.MAIN])
 includeProject(":room:room-rxjava2", "room/rxjava2", [BuildType.MAIN])
 includeProject(":room:room-rxjava3", "room/rxjava3", [BuildType.MAIN])
@@ -534,6 +537,7 @@
 includeProject(":savedstate:savedstate", "savedstate/savedstate", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR])
 includeProject(":savedstate:savedstate-ktx", "savedstate/savedstate-ktx", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":security:security-app-authenticator", "security/security-app-authenticator", [BuildType.MAIN])
+includeProject(":security:security-app-authenticator-testing", "security/security-app-authenticator-testing", [BuildType.MAIN])
 includeProject(":security:security-biometric", "security/security-biometric", [BuildType.MAIN])
 includeProject(":security:security-crypto", "security/crypto", [BuildType.MAIN])
 includeProject(":security:security-crypto-ktx", "security/security-crypto-ktx", [BuildType.MAIN])
diff --git a/testutils/testutils-navigation/src/androidTest/java/androidx/testutils/TestNavigatorDestinationBuilderTest.kt b/testutils/testutils-navigation/src/androidTest/java/androidx/testutils/TestNavigatorDestinationBuilderTest.kt
index fb2d187..20383a3 100644
--- a/testutils/testutils-navigation/src/androidTest/java/androidx/testutils/TestNavigatorDestinationBuilderTest.kt
+++ b/testutils/testutils-navigation/src/androidTest/java/androidx/testutils/TestNavigatorDestinationBuilderTest.kt
@@ -31,6 +31,7 @@
 class TestNavigatorDestinationBuilderTest {
     private val provider = TestNavigatorProvider()
 
+    @Suppress("DEPRECATION")
     @Test
     fun test() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
@@ -53,6 +54,7 @@
         )
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun testWithBody() {
         val graph = provider.navigation(startDestination = DESTINATION_ID) {
diff --git a/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt b/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt
index f8b90e7..a9bcbad 100644
--- a/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt
+++ b/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt
@@ -37,6 +37,7 @@
 /**
  * Construct a new [TestNavigator.Destination]
  */
+@Suppress("DEPRECATION")
 inline fun NavGraphBuilder.test(
     @IdRes id: Int,
     builder: TestNavigatorDestinationBuilder.() -> Unit
@@ -62,6 +63,7 @@
  */
 @NavDestinationDsl
 class TestNavigatorDestinationBuilder : NavDestinationBuilder<TestNavigator.Destination> {
+    @Suppress("DEPRECATION")
     constructor(navigator: TestNavigator, @IdRes id: Int = 0) : super(navigator, id)
     constructor(navigator: TestNavigator, route: String) : super(navigator, route)
 }
diff --git a/transition/transition-ktx/build.gradle b/transition/transition-ktx/build.gradle
index 27caf0b..73f8be9 100644
--- a/transition/transition-ktx/build.gradle
+++ b/transition/transition-ktx/build.gradle
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
 plugins {
@@ -26,14 +24,14 @@
 }
 
 dependencies {
-    api(KOTLIN_STDLIB)
+    api(libs.kotlinStdlib)
     api(project(":transition:transition"))
 
-    androidTestImplementation(JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
 }
 
 androidx {
diff --git a/transition/transition/build.gradle b/transition/transition/build.gradle
index eb608cb..1b20ed2 100644
--- a/transition/transition/build.gradle
+++ b/transition/transition/build.gradle
@@ -1,9 +1,6 @@
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -17,15 +14,15 @@
     compileOnly("androidx.fragment:fragment:1.2.5")
     compileOnly("androidx.appcompat:appcompat:1.0.1")
 
-    androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.espressoCore, excludes.espresso)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(project(":fragment:fragment"))
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0")
     androidTestImplementation(project(":internal-testutils-runtime"), {
diff --git a/versionedparcelable/versionedparcelable-compiler/build.gradle b/versionedparcelable/versionedparcelable-compiler/build.gradle
index 72cbdae..83f50b6 100644
--- a/versionedparcelable/versionedparcelable-compiler/build.gradle
+++ b/versionedparcelable/versionedparcelable-compiler/build.gradle
@@ -18,15 +18,13 @@
 import androidx.build.LibraryType
 import androidx.build.LibraryVersions
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 plugins {
     id("AndroidXPlugin")
     id("java-library")
 }
 
 dependencies {
-    implementation(JAVAPOET)
+    implementation(libs.javapoet)
 }
 
 androidx {
diff --git a/versionedparcelable/versionedparcelable/build.gradle b/versionedparcelable/versionedparcelable/build.gradle
index 5ffba1b..b093782 100644
--- a/versionedparcelable/versionedparcelable/build.gradle
+++ b/versionedparcelable/versionedparcelable/build.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
-
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
@@ -29,12 +27,12 @@
     api("androidx.annotation:annotation:1.2.0")
     implementation("androidx.collection:collection:1.0.0")
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
-    androidTestImplementation(TRUTH)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
+    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
+    androidTestImplementation(libs.truth)
     androidTestAnnotationProcessor project(":versionedparcelable:versionedparcelable-compiler")
 }
 
diff --git a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
index addff08..defc993 100644
--- a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
+++ b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.CornerSize
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
@@ -26,6 +27,7 @@
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
@@ -37,6 +39,7 @@
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.getUnclippedBoundsInRoot
 import androidx.compose.ui.test.junit4.ComposeContentTestRule
@@ -45,6 +48,7 @@
 import androidx.compose.ui.test.onChildAt
 import androidx.compose.ui.test.onNodeWithContentDescription
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performClick
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.Dp
@@ -254,6 +258,110 @@
             .assertTopPositionInRootIsEqualTo((itemBounds.height - iconBounds.height) / 2)
     }
 
+    @Test
+    fun icon_only_compact_chip_has_correct_default_width_and_height() {
+        val iconTag = "TestIcon"
+        val chipTag = "chip"
+        rule
+            .setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+                CompactChip(
+                    onClick = {},
+                    modifier = Modifier.testTag(chipTag),
+                    icon = { CreateImage(iconTag) }
+                )
+            }
+
+        rule.onRoot().assertWidthIsEqualTo(52.dp).assertHeightIsEqualTo(32.dp)
+    }
+
+    @Test
+    fun label_only_compact_chip_has_correct_default_height() {
+        val chipTag = "chip"
+        rule
+            .setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+                CompactChip(
+                    onClick = {},
+                    modifier = Modifier.testTag(chipTag),
+                    label = { Text("Test") }
+                )
+            }
+
+        rule.onRoot().assertHeightIsEqualTo(32.dp)
+    }
+
+    @Test
+    fun no_content_compact_chip_has_correct_default_width_and_height() {
+        val chipTag = "chip"
+        rule
+            .setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+                CompactChip(
+                    onClick = {},
+                    modifier = Modifier.testTag(chipTag),
+                )
+            }
+
+        rule.onRoot().assertWidthIsEqualTo(52.dp).assertHeightIsEqualTo(32.dp)
+    }
+
+    @Test
+    fun icon_only_compact_chip_can_have_width_overridden() {
+        val iconTag = "TestIcon"
+        val chipTag = "chip"
+        rule
+            .setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+                CompactChip(
+                    onClick = {},
+                    modifier = Modifier
+                        .testTag(chipTag)
+                        .width(100.dp),
+                    icon = { CreateImage(iconTag) }
+                )
+            }
+
+        rule.onRoot().assertWidthIsEqualTo(100.dp)
+    }
+
+    @Test
+    fun has_icon_in_correct_location_when_compact_chip() {
+        val iconTag = "TestIcon"
+        val chipTag = "chip"
+        rule
+            .setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+                CompactChip(
+                    onClick = {},
+                    label = { Text("Blue green orange") },
+                    icon = { CreateImage(iconTag) },
+                    modifier = Modifier.testTag(chipTag)
+                )
+            }
+        val itemBounds = rule.onNodeWithTag(chipTag).getUnclippedBoundsInRoot()
+        val iconBounds = rule.onNodeWithTag(iconTag, useUnmergedTree = true)
+            .getUnclippedBoundsInRoot()
+
+        rule.onNodeWithContentDescription(iconTag, useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo((itemBounds.height - iconBounds.height) / 2)
+    }
+
+    @Test
+    fun has_icon_in_correct_location_when_icon_only_chip() {
+        val iconTag = "TestIcon"
+        val chipTag = "chip"
+        rule
+            .setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+                CompactChip(
+                    onClick = {},
+                    modifier = Modifier.testTag(chipTag),
+                    icon = { CreateImage(iconTag) }
+                )
+            }
+        val itemBounds = rule.onNodeWithTag(chipTag).getUnclippedBoundsInRoot()
+        val iconBounds = rule.onNodeWithTag(iconTag, useUnmergedTree = true)
+            .getUnclippedBoundsInRoot()
+
+        rule.onNodeWithContentDescription(iconTag, useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo((itemBounds.height - iconBounds.height) / 2)
+    }
+
     private fun verifyHeight(expectedHeight: Dp) {
         rule.verifyHeight(expectedHeight) {
             Chip(
@@ -276,12 +384,12 @@
             TestChipColors.Primary,
             ChipStatus.Enabled,
             { MaterialTheme.colors.primary },
-            { MaterialTheme.colors.onPrimary }
+            { MaterialTheme.colors.onPrimary },
         )
 
     @Test
     fun three_slot_layout_gives_primary_enabled_colors() =
-        verifyThreeSlotColors(
+        verifySlotColors(
             TestChipColors.Primary,
             ChipStatus.Enabled,
             { MaterialTheme.colors.primary },
@@ -291,17 +399,29 @@
         )
 
     @Test
+    fun compact_chip_gives_primary_enabled_colors() =
+        verifySlotColors(
+            TestChipColors.Primary,
+            ChipStatus.Enabled,
+            { MaterialTheme.colors.primary },
+            { MaterialTheme.colors.onPrimary },
+            { MaterialTheme.colors.onPrimary },
+            { MaterialTheme.colors.onPrimary },
+            compactChip = true
+        )
+
+    @Test
     fun gives_primary_disabled_colors() =
         verifyColors(
             TestChipColors.Primary,
             ChipStatus.Disabled,
             { MaterialTheme.colors.primary },
-            { MaterialTheme.colors.onPrimary }
+            { MaterialTheme.colors.onPrimary },
         )
 
     @Test
     fun three_slot_layout_gives_primary_disabled_colors() =
-        verifyThreeSlotColors(
+        verifySlotColors(
             TestChipColors.Primary,
             ChipStatus.Disabled,
             { MaterialTheme.colors.primary },
@@ -321,7 +441,7 @@
 
     @Test
     fun three_slot_layout_gives_secondary_enabled_colors() =
-        verifyThreeSlotColors(
+        verifySlotColors(
             TestChipColors.Secondary,
             ChipStatus.Enabled,
             { MaterialTheme.colors.surface },
@@ -341,7 +461,7 @@
 
     @Test
     fun three_slot_layout_gives_secondary_disabled_colors() =
-        verifyThreeSlotColors(
+        verifySlotColors(
             TestChipColors.Secondary,
             ChipStatus.Enabled,
             { MaterialTheme.colors.surface },
@@ -351,6 +471,18 @@
         )
 
     @Test
+    fun compact_chip_gives_secondary_disabled_colors() =
+        verifySlotColors(
+            TestChipColors.Secondary,
+            ChipStatus.Enabled,
+            { MaterialTheme.colors.surface },
+            { MaterialTheme.colors.onSurface },
+            { MaterialTheme.colors.onSurface },
+            { MaterialTheme.colors.onSurface },
+            compactChip = true
+        )
+
+    @Test
     fun allows_custom_enabled_background_color_override() {
         val overrideColor = Color.Yellow
         rule.setContentWithTheme {
@@ -497,16 +629,22 @@
         var expectedBackground = Color.Transparent
         var expectedContent = Color.Transparent
         var actualContent = Color.Transparent
-        var expectedAlpha = 0.0f
+        val testBackground = Color.White
 
         rule.setContentWithTheme {
-            expectedBackground = backgroundColor()
-            expectedContent = contentColor()
-            expectedAlpha = ContentAlpha.disabled
+            if (status.enabled()) {
+                expectedBackground = backgroundColor()
+                expectedContent = contentColor()
+            } else {
+                expectedBackground =
+                    backgroundColor().copy(alpha = ContentAlpha.disabled)
+                        .compositeOver(testBackground)
+                expectedContent = contentColor().copy(alpha = ContentAlpha.disabled)
+            }
             Box(
                 modifier = Modifier
                     .fillMaxSize()
-                    .background(expectedBackground)
+                    .background(testBackground)
             ) {
                 Chip(
                     onClick = {},
@@ -518,26 +656,24 @@
             }
         }
 
-        if (status.enabled()) {
-            assertEquals(expectedContent, actualContent)
-        } else {
-            assertEquals(expectedContent.copy(alpha = expectedAlpha), actualContent)
-        }
+        assertEquals(expectedContent, actualContent)
 
-        if (expectedBackground != Color.Transparent) {
-            rule.onNodeWithTag("test-item").onChildAt(0)
-                .captureToImage()
-                .assertContainsColor(expectedBackground, 50.0f)
-        }
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertContainsColor(
+                if (expectedBackground != Color.Transparent) expectedBackground else testBackground,
+                50.0f
+            )
     }
 
-    private fun verifyThreeSlotColors(
+    private fun verifySlotColors(
         testChipColors: TestChipColors,
         status: ChipStatus,
         backgroundColor: @Composable () -> Color,
         contentColor: @Composable () -> Color,
         secondaryContentColor: @Composable () -> Color,
-        iconColor: @Composable () -> Color
+        iconColor: @Composable () -> Color,
+        compactChip: Boolean = false
     ) {
         var expectedBackground = Color.Transparent
         var expectedContent = Color.Transparent
@@ -546,49 +682,63 @@
         var actualContent = Color.Transparent
         var actualSecondaryContent = Color.Transparent
         var actualIcon = Color.Transparent
-        var expectedAlpha = 0.0f
+        val testBackground = Color.White
 
         rule.setContentWithTheme {
-            expectedBackground = backgroundColor()
-            expectedContent = contentColor()
-            expectedSecondaryContent = secondaryContentColor()
-            expectedIcon = iconColor()
-            expectedAlpha = ContentAlpha.disabled
+            if (status.enabled()) {
+                expectedBackground = backgroundColor()
+                expectedContent = contentColor()
+                expectedSecondaryContent = secondaryContentColor()
+                expectedIcon = iconColor()
+            } else {
+                expectedBackground =
+                    backgroundColor().copy(alpha = ContentAlpha.disabled)
+                        .compositeOver(testBackground)
+                expectedContent = contentColor().copy(alpha = ContentAlpha.disabled)
+                expectedSecondaryContent = secondaryContentColor()
+                    .copy(alpha = ContentAlpha.disabled)
+                expectedIcon = iconColor().copy(alpha = ContentAlpha.disabled)
+            }
             Box(
                 modifier = Modifier
                     .fillMaxSize()
-                    .background(expectedBackground)
+                    .background(testBackground)
             ) {
-                Chip(
-                    onClick = {},
-                    colors = testChipColors.chipColors(),
-                    label = { actualContent = LocalContentColor.current },
-                    secondaryLabel = { actualSecondaryContent = LocalContentColor.current },
-                    icon = { actualIcon = LocalContentColor.current },
-                    enabled = status.enabled(),
-                    modifier = Modifier.testTag("test-item")
-                )
+                if (compactChip) {
+                    CompactChip(
+                        onClick = {},
+                        colors = testChipColors.chipColors(),
+                        label = { actualContent = LocalContentColor.current },
+                        icon = { actualIcon = LocalContentColor.current },
+                        enabled = status.enabled(),
+                        modifier = Modifier.testTag("test-item")
+                    )
+                } else {
+                    Chip(
+                        onClick = {},
+                        colors = testChipColors.chipColors(),
+                        label = { actualContent = LocalContentColor.current },
+                        secondaryLabel = { actualSecondaryContent = LocalContentColor.current },
+                        icon = { actualIcon = LocalContentColor.current },
+                        enabled = status.enabled(),
+                        modifier = Modifier.testTag("test-item")
+                    )
+                }
             }
         }
 
-        if (status.enabled()) {
-            assertEquals(expectedContent, actualContent)
+        assertEquals(expectedContent, actualContent)
+        if (! compactChip) {
             assertEquals(expectedSecondaryContent, actualSecondaryContent)
-            assertEquals(expectedIcon, actualIcon)
-        } else {
-            assertEquals(expectedContent.copy(alpha = expectedAlpha), actualContent)
-            assertEquals(
-                expectedSecondaryContent.copy(alpha = expectedAlpha),
-                actualSecondaryContent
-            )
-            assertEquals(expectedIcon.copy(alpha = expectedAlpha), actualIcon)
         }
+        assertEquals(expectedIcon, actualIcon)
 
-        if (expectedBackground != Color.Transparent) {
-            rule.onNodeWithTag("test-item").onChildAt(0)
-                .captureToImage()
-                .assertContainsColor(expectedBackground, 50.0f)
-        }
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertContainsColor(
+                if (expectedBackground != Color.Transparent) expectedBackground else testBackground,
+                50.0f
+            )
     }
 }
 
diff --git a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/MaterialThemeTest.kt b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/MaterialThemeTest.kt
new file mode 100644
index 0000000..32c54d7
--- /dev/null
+++ b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/MaterialThemeTest.kt
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2021 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.wear.compose.material
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+
+class MaterialThemeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun sets_default_color() {
+        var expectedBackground = Color.Transparent
+        rule.setContentWithTheme {
+            expectedBackground = MaterialTheme.colors.primary
+            Chip(
+                onClick = {},
+                colors = ChipDefaults.primaryChipColors(), // make sure using the primary color
+                label = { Text("Test") },
+            )
+        }
+
+        rule.onNodeWithText("Test")
+            .captureToImage()
+            .assertContainsColor(expectedBackground, 50.0f)
+    }
+
+    @Test
+    fun overrides_color_when_nested() {
+        // MaterialTheme in 'setWearContent' sets the primary background
+        // to cornflower blue. The nested theme should override that for primary.
+        rule.setContentWithTheme {
+            MaterialTheme(colors = MaterialTheme.colors.copy(primary = Color.Cyan)) {
+                Chip(
+                    onClick = {},
+                    colors = ChipDefaults.primaryChipColors(), // make sure using the primary color
+                    label = { Text("Test") },
+                )
+            }
+        }
+
+        rule.onNodeWithText("Test")
+            .captureToImage()
+            .assertContainsColor(Color.Cyan, 50.0f)
+    }
+
+    @Test
+    fun can_be_overridden_by_component_color_explicitly() {
+        rule.setContentWithTheme {
+            Chip(
+                onClick = {},
+                colors = ChipDefaults.primaryChipColors(backgroundColor = Color.Yellow),
+                label = { Text("Test") },
+            )
+        }
+
+        rule.onNodeWithText("Test")
+            .captureToImage()
+            .assertContainsColor(Color.Yellow, 50.0f)
+    }
+
+    @Test
+    fun sets_default_textstyle() {
+        var expectedStyle: TextStyle? = null
+
+        rule.setContentWithTheme {
+            expectedStyle = MaterialTheme.typography.button
+            Chip(
+                onClick = {},
+                label = { Text("Test") },
+            )
+        }
+
+        assertTextStyleEquals(expectedStyle!!, rule.textStyleOf("Test"))
+    }
+
+    @Test
+    fun overrides_textstyle_when_nested() {
+        val override = TextStyle(
+            fontFamily = FontFamily.Default,
+            fontWeight = FontWeight.Normal,
+            fontSize = 16.sp,
+            letterSpacing = 0.sp
+        )
+        rule.setContentWithTheme {
+            MaterialTheme(
+                typography = MaterialTheme.typography
+                    .copy(button = override)
+            ) {
+                Chip(
+                    onClick = {},
+                    label = { Text("Test") },
+                )
+            }
+        }
+
+        assertTextStyleEquals(override, rule.textStyleOf("Test"))
+    }
+
+    @Test
+    fun sets_primary_color_dynamically() =
+        verifyBackgroundColorIsDynamic(
+            initial = { MaterialTheme.colors.primary },
+            selectChipColors = { ChipDefaults.primaryChipColors() },
+            updateThemeColors = { colors, primary -> colors.copy(primary = primary) }
+        )
+
+    @Test
+    fun sets_primaryvariant_color_dynamically() =
+        verifyBackgroundColorIsDynamic(
+            initial = { MaterialTheme.colors.primaryVariant },
+            selectChipColors = {
+                ChipDefaults
+                    .primaryChipColors(backgroundColor = MaterialTheme.colors.primaryVariant)
+            },
+            updateThemeColors =
+                { colors, primaryVariant -> colors.copy(primaryVariant = primaryVariant) }
+        )
+
+    @Test
+    fun sets_secondary_color_dynamically() =
+        verifyBackgroundColorIsDynamic(
+            initial = { MaterialTheme.colors.secondary },
+            selectChipColors = {
+                ChipDefaults
+                    .secondaryChipColors(backgroundColor = MaterialTheme.colors.secondary)
+            },
+            updateThemeColors = { colors, secondary -> colors.copy(secondary = secondary) }
+        )
+
+    @Test
+    fun sets_secondaryvariant_color_dynamically() =
+        verifyBackgroundColorIsDynamic(
+            initial = { MaterialTheme.colors.secondaryVariant },
+            selectChipColors = {
+                ChipDefaults
+                    .secondaryChipColors(backgroundColor = MaterialTheme.colors.secondaryVariant)
+            },
+            updateThemeColors =
+                { colors, secondaryVariant -> colors.copy(secondaryVariant = secondaryVariant) }
+        )
+
+    @Test
+    fun sets_error_color_dynamically() =
+        verifyBackgroundColorIsDynamic(
+            initial = { MaterialTheme.colors.error },
+            selectChipColors = {
+                ChipDefaults
+                    .secondaryChipColors(backgroundColor = MaterialTheme.colors.error)
+            },
+            updateThemeColors = { colors, error -> colors.copy(error = error) }
+        )
+
+    @Test
+    fun sets_colors_dynamically() {
+        var initialBackground = Color.Transparent
+        val overrideBackground = Color.Cyan
+
+        rule.setContentWithTheme {
+            initialBackground = MaterialTheme.colors.primary
+            val colors = Colors()
+            val rememberedColors = remember { mutableStateOf(colors) }
+            MaterialTheme(colors = rememberedColors.value) {
+                Column {
+                    Chip(
+                        onClick = {},
+                        colors = ChipDefaults.primaryChipColors(),
+                        label = { Text("Test") },
+                    )
+                    Chip(
+                        onClick = {
+                            rememberedColors.value = colors.copy(primary = overrideBackground)
+                        },
+                        label = { },
+                        modifier = Modifier.testTag("button")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithText("Test")
+            .captureToImage()
+            .assertContainsColor(initialBackground, 60.0f)
+        rule.onNodeWithTag("button")
+            .performClick()
+        rule.onNodeWithText("Test")
+            .captureToImage()
+            .assertContainsColor(overrideBackground, 60.0f)
+    }
+
+    @Test
+    fun sets_button_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.button },
+            updateTextStyle = { typography, button -> typography.copy(button = button) }
+        )
+
+    @Test
+    fun sets_display1_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.display1 },
+            updateTextStyle = { typography, display1 -> typography.copy(display1 = display1) }
+        )
+
+    @Test
+    fun sets_display2_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.display2 },
+            updateTextStyle = { typography, display2 -> typography.copy(display2 = display2) }
+        )
+
+    @Test
+    fun sets_display3_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.display3 },
+            updateTextStyle = { typography, display3 -> typography.copy(display3 = display3) }
+        )
+
+    @Test
+    fun sets_title1_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.title1 },
+            updateTextStyle = { typography, title1 -> typography.copy(title1 = title1) }
+        )
+
+    @Test
+    fun sets_title2_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.title2 },
+            updateTextStyle = { typography, title2 -> typography.copy(title2 = title2) }
+        )
+
+    @Test
+    fun sets_title3_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.title3 },
+            updateTextStyle = { typography, title3 -> typography.copy(title3 = title3) }
+        )
+
+    @Test
+    fun sets_body1_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.body1 },
+            updateTextStyle = { typography, body1 -> typography.copy(body1 = body1) }
+        )
+
+    @Test
+    fun sets_body2_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.body2 },
+            updateTextStyle = { typography, body2 -> typography.copy(body2 = body2) }
+        )
+
+    @Test
+    fun sets_caption1_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.caption1 },
+            updateTextStyle = { typography, caption1 -> typography.copy(caption1 = caption1) }
+        )
+
+    @Test
+    fun sets_caption2_textstyle_dynamically() =
+        verifyTextStyleIsDynamic(
+            selectStyle = { it.caption2 },
+            updateTextStyle = { typography, caption2 -> typography.copy(caption2 = caption2) }
+        )
+
+    @Test
+    fun sets_typography_dynamically() {
+        val initialStyle = TextStyle(
+            fontFamily = FontFamily.Default,
+            fontWeight = FontWeight.Bold,
+            fontSize = 14.sp,
+            letterSpacing = 0.sp
+        )
+        val overrideTextStyle = TextStyle(
+            fontFamily = FontFamily.Default,
+            fontWeight = FontWeight.Normal,
+            fontSize = 8.sp,
+            letterSpacing = 0.sp
+        )
+
+        rule.setContentWithTheme {
+            val typography = Typography()
+            val rememberedTypography = remember { mutableStateOf(typography) }
+            MaterialTheme(typography = rememberedTypography.value) {
+                Column {
+                    Chip(
+                        onClick = {},
+                        colors = ChipDefaults.primaryChipColors(),
+                        label = { Text("Test") },
+                    )
+                    Chip(
+                        onClick = {
+                            rememberedTypography.value =
+                                typography.copy(button = overrideTextStyle)
+                        },
+                        label = { },
+                        modifier = Modifier.testTag("button")
+                    )
+                }
+            }
+        }
+
+        assertTextStyleEquals(initialStyle, rule.textStyleOf("Test"))
+        rule.onNodeWithTag("button").performClick()
+        assertTextStyleEquals(overrideTextStyle, rule.textStyleOf("Test"))
+    }
+
+    private fun verifyBackgroundColorIsDynamic(
+        initial: @Composable () -> Color,
+        selectChipColors: @Composable () -> ChipColors,
+        updateThemeColors: (Colors, Color) -> Colors
+    ) {
+        var initialColor = Color.Transparent
+        val overrideColor = Color.Cyan
+        val colors = Colors()
+
+        rule.setContentWithTheme {
+            initialColor = initial()
+            val dynamicColor = remember { mutableStateOf(initialColor) }
+            val themeColors = updateThemeColors(colors, dynamicColor.value)
+            MaterialTheme(colors = themeColors) {
+                Column {
+                    Chip(
+                        onClick = {},
+                        colors = selectChipColors(),
+                        label = { Text("Test") },
+                    )
+                    Chip(
+                        onClick = { dynamicColor.value = overrideColor },
+                        label = { },
+                        modifier = Modifier.testTag("button")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithText("Test")
+            .captureToImage()
+            .assertContainsColor(initialColor, 60.0f)
+        rule.onNodeWithTag("button")
+            .performClick()
+        rule.onNodeWithText("Test")
+            .captureToImage()
+            .assertContainsColor(overrideColor, 60.0f)
+    }
+
+    private fun verifyContentColorIsDynamic(
+        initial: @Composable () -> Color,
+        selectChipColors: @Composable () -> ChipColors,
+        updateThemeColors: (Colors, Color) -> Colors
+    ) {
+        var initialColor = Color.White
+        val overrideColor = Color.Cyan
+        val colors = Colors()
+
+        rule.setContentWithTheme {
+            initialColor = initial()
+            val dynamicColor = remember { mutableStateOf(initialColor) }
+            val themeColors = updateThemeColors(colors, dynamicColor.value)
+            MaterialTheme(colors = themeColors) {
+                Column {
+                    Chip(
+                        onClick = {},
+                        colors = selectChipColors(),
+                        label = { Text("Test") },
+                    )
+                    Chip(
+                        onClick = { dynamicColor.value = overrideColor },
+                        label = { Text("Test") },
+                        modifier = Modifier.testTag("button")
+                    )
+                }
+            }
+        }
+
+        assertEquals(initialColor, rule.textStyleOf("Test").color)
+        rule.onNodeWithTag("button")
+            .performClick()
+        assertEquals(overrideColor, rule.textStyleOf("Test").color)
+    }
+
+    private fun verifyTextStyleIsDynamic(
+        selectStyle: (Typography) -> TextStyle,
+        updateTextStyle: (Typography, TextStyle) -> Typography
+    ) {
+        var initialStyle = TextStyle()
+        val overrideTextStyle = TextStyle(
+            fontFamily = FontFamily.Default,
+            fontWeight = FontWeight.Normal,
+            fontSize = 8.sp,
+            letterSpacing = 0.sp
+        )
+        val typography = Typography()
+
+        rule.setContentWithTheme {
+            initialStyle = selectStyle(typography)
+            val dynamicStyle = remember { mutableStateOf(initialStyle) }
+            val rememberedTypography =
+                updateTextStyle(typography, dynamicStyle.value)
+            MaterialTheme(
+                // WearChip always uses 'button' style for text, so assign the style under test to button.
+                typography = rememberedTypography.copy(button = selectStyle(rememberedTypography))
+            ) {
+                Column {
+                    Chip(
+                        onClick = {},
+                        label = { Text("Test") },
+                    )
+                    Chip(
+                        onClick = { dynamicStyle.value = overrideTextStyle },
+                        label = { },
+                        modifier = Modifier.testTag("button")
+                    )
+                }
+            }
+        }
+
+        assertTextStyleEquals(initialStyle, rule.textStyleOf("Test"))
+        rule.onNodeWithTag("button").performClick()
+        assertTextStyleEquals(overrideTextStyle, rule.textStyleOf("Test"))
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt b/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt
index 35e949e..75b21b3 100644
--- a/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt
+++ b/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt
@@ -15,8 +15,7 @@
  */
 package androidx.wear.compose.material
 
-import androidx.compose.foundation.Indication
-import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -29,9 +28,9 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.material.ContentAlpha
-import androidx.compose.material.Surface
+import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
@@ -41,6 +40,7 @@
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.draw.paint
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
@@ -75,7 +75,6 @@
  * @param modifier Modifier to be applied to the chip
  * @param enabled Controls the enabled state of the chip. When `false`, this chip will not
  * be clickable
- * @param onClickLabel Semantic / accessibility label for the [onClick] action
  * @param contentPadding The spacing values to apply internally between the container and the
  * content
  * @param shape Defines the chip's shape. It is strongly recommended to use the default as this
@@ -84,9 +83,6 @@
  * [Interaction]s for this Chip. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Chip in different [Interaction]s.
- * @param indication Indication to be shown when surface is pressed. By default, indication from
- * [LocalIndication] will be used. Pass `null` to show no indication, or current value from
- * [LocalIndication] to show theme default
  * @param role The type of user interface element. Accessibility services might use this
  * to describe the element or do customizations
  */
@@ -96,19 +92,17 @@
     colors: ChipColors,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
-    onClickLabel: String? = null,
     contentPadding: PaddingValues = ChipDefaults.ContentPadding,
     shape: Shape = MaterialTheme.shapes.small,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    indication: Indication? = LocalIndication.current,
     role: Role? = Role.Button,
     content: @Composable () -> Unit,
 ) {
-    Surface(
+    Box(
         modifier = modifier
-            .height(ChipDefaults.Height),
-        color = Color.Transparent,
-        shape = shape,
+            .height(ChipDefaults.Height)
+            .clip(shape = shape)
+            .background(color = Color.Transparent, shape = shape)
     ) {
         // TODO: Due to b/178201337 the paint() modifier on the box doesn't make a call to draw the
         //  box contents. As a result we need to have stacked boxes to enable us to paint the
@@ -122,12 +116,12 @@
         val contentBoxModifier = Modifier
             .clickable(
                 enabled = enabled,
-                onClickLabel = onClickLabel,
                 onClick = onClick,
                 role = role,
-                indication = indication,
+                indication = rememberRipple(),
                 interactionSource = interactionSource,
             )
+            .fillMaxSize()
             .padding(contentPadding)
 
         Box(
@@ -139,6 +133,7 @@
             CompositionLocalProvider(
                 LocalContentColor provides colors.contentColor(enabled = enabled).value,
                 LocalTextStyle provides MaterialTheme.typography.button,
+                LocalContentAlpha provides colors.contentColor(enabled = enabled).value.alpha,
                 content = content
             )
         }
@@ -148,7 +143,7 @@
 /**
  * Wear Material [Chip] that offers three slots and a specific layout for an icon, label and
  * secondaryLabel. The icon and secondaryLabel are optional. The items are laid out with the icon,
- * if provided, a the start of a row, with a column next containing the two label slots.
+ * if provided, at the start of a row, with a column next containing the two label slots.
  *
  * The [Chip] is Stadium shaped and has a max height designed to take no more than two lines of text
  * of [Typography.button] style. If no secondary label is provided then the label
@@ -167,26 +162,25 @@
  *
  * Chips can be enabled or disabled. A disabled chip will not respond to click events.
  *
- * @param label A slot for providing the chips main label. The contents are expected to be text
+ * @param label A slot for providing the chip's main label. The contents are expected to be text
  * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
  * @param onClick Will be called when the user clicks the chip
  * @param modifier Modifier to be applied to the chip
- * @param secondaryLabel A slot for providing the chips secondary label. The contents are expected
+ * @param secondaryLabel A slot for providing the chip's secondary label. The contents are expected
  * to be text which is "start" aligned if there is an icon preset and "start" or "center" aligned if
  * not. label and secondaryLabel contents should be consistently aligned.
+ * @param icon A slot for providing the chip's icon. The contents are expected to be a horizontally
+ * and vertically aligned icon of size [ChipDefaults.IconSize] or [ChipDefaults.LargeIconSize].
  * @param colors [ChipColors] that will be used to resolve the background and content color for
  * this chip in different states. See [ChipDefaults.chipColors]. Defaults to
  * [ChipDefaults.primaryChipColors]
  * @param enabled Controls the enabled state of the chip. When `false`, this chip will not
  * be clickable
- * @param onClickLabel Semantic / accessibility label for the [onClick] action
  * @param interactionSource The [MutableInteractionSource] representing the stream of
  * [Interaction]s for this Chip. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Chip in different [Interaction]s.
- * @param indication Indication to be shown when surface is pressed. By default, indication from
- * [LocalIndication] will be used. Pass `null` to show no indication, or current value from
- * [LocalIndication] to show theme default
+
  * @param contentPadding The spacing values to apply internally between the container and the
  * content
  */
@@ -199,9 +193,7 @@
     icon: (@Composable () -> Unit)? = null,
     colors: ChipColors = ChipDefaults.primaryChipColors(),
     enabled: Boolean = true,
-    onClickLabel: String? = null,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    indication: Indication? = LocalIndication.current,
     contentPadding: PaddingValues = ChipDefaults.contentPadding(icon != null),
 ) {
     Chip(
@@ -209,9 +201,7 @@
         colors = colors,
         modifier = modifier,
         enabled = enabled,
-        onClickLabel = onClickLabel,
         interactionSource = interactionSource,
-        indication = indication,
         contentPadding = contentPadding
     ) {
         Row(
@@ -224,6 +214,8 @@
                 ) {
                     CompositionLocalProvider(
                         LocalContentColor provides colors.iconTintColor(enabled).value,
+                        LocalContentAlpha provides
+                            colors.iconTintColor(enabled = enabled).value.alpha,
                         content = icon
                     )
                 }
@@ -233,12 +225,15 @@
                 CompositionLocalProvider(
                     LocalContentColor provides colors.contentColor(enabled).value,
                     LocalTextStyle provides MaterialTheme.typography.button,
+                    LocalContentAlpha provides colors.contentColor(enabled = enabled).value.alpha,
                     content = label
                 )
                 if (secondaryLabel != null) {
                     CompositionLocalProvider(
                         LocalContentColor provides colors.secondaryContentColor(enabled).value,
                         LocalTextStyle provides MaterialTheme.typography.button,
+                        LocalContentAlpha provides
+                            colors.secondaryContentColor(enabled = enabled).value.alpha,
                         content = secondaryLabel
                     )
                 }
@@ -248,6 +243,94 @@
 }
 
 /**
+ * A compact Wear Material Chip that offers two slots and a specific layout for an icon and label.
+ * Both the icon and label are optional however it is expected that at least one will be provided.
+ *
+ * The [CompactChip] is Stadium shaped and has a max height designed to take no more than one line
+ * of text of [Typography.button] style and/or one 24x24 icon. The default max height is
+ * [ChipDefaults.CompactChipHeight].
+ *
+ * If a icon is provided then the labels should be "start" aligned, e.g. left aligned in ltr so that
+ * the text starts next to the icon.
+ *
+ * The items are laid out as follows.
+ *
+ * 1. If a label is provided then the chip will be laid out with the optional icon at the start of a
+ * row followed by the label with a default max height of [ChipDefaults.CompactChipHeight].
+ *
+ * 2. If only an icon is provided it will be laid out vertically and horizontally centered with a
+ * default height of [ChipDefaults.CompactChipHeight] and the default width of
+ * [ChipDefaults.IconOnlyCompactChipWidth]
+ *
+ * The [CompactChip] can have different styles with configurable content colors, background colors
+ * including gradients, these are provided by [ChipColors] implementations.
+ *
+ * The recommended set of [ChipColors] styles can be obtained from [ChipDefaults], e.g.
+ * [ChipDefaults.primaryChipColors] to get a color scheme for a primary [Chip] which by default
+ * will have a solid background of [Colors.primary] and content color of
+ * [Colors.onPrimary].
+ *
+ * Chips can be enabled or disabled. A disabled chip will not respond to click events.
+ *
+ * @param onClick Will be called when the user clicks the chip
+ * @param modifier Modifier to be applied to the chip
+ * @param label A slot for providing the chip's main label. The contents are expected to be text
+ * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
+ * @param icon A slot for providing the chip's icon. The contents are expected to be a horizontally
+ * and vertically aligned icon of size [ChipDefaults.IconSize] or [ChipDefaults.LargeIconSize].
+ * @param colors [ChipColors] that will be used to resolve the background and content color for
+ * this chip in different states. See [ChipDefaults.chipColors]. Defaults to
+ * [ChipDefaults.primaryChipColors]
+ * @param enabled Controls the enabled state of the chip. When `false`, this chip will not
+ * be clickable
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this Chip. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this Chip in different [Interaction]s.
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ */
+@Composable
+fun CompactChip(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    label: (@Composable () -> Unit)? = null,
+    icon: (@Composable () -> Unit)? = null,
+    colors: ChipColors = ChipDefaults.primaryChipColors(),
+    enabled: Boolean = true,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    contentPadding: PaddingValues = ChipDefaults.contentPadding(icon != null),
+) {
+    if (label != null) {
+        Chip(
+            label = label,
+            onClick = onClick,
+            modifier = modifier.height(ChipDefaults.CompactChipHeight),
+            icon = icon,
+            colors = colors,
+            enabled = enabled,
+            interactionSource = interactionSource,
+            contentPadding = contentPadding
+        )
+    } else {
+        Chip(
+            onClick = onClick,
+            modifier = modifier
+                .height(ChipDefaults.CompactChipHeight)
+                .width(ChipDefaults.IconOnlyCompactChipWidth),
+            colors = colors,
+            enabled = enabled,
+            interactionSource = interactionSource,
+            contentPadding = contentPadding
+        ) {
+            if (icon != null) {
+                icon()
+            }
+        }
+    }
+}
+
+/**
  * Represents the background and content colors used in a chip in different states.
  *
  * See [ChipDefaults.primaryChipColors] for the default colors used in a primary styled [Chip].
@@ -377,6 +460,18 @@
     internal val Height = 52.dp
 
     /**
+     * The height applied for the [CompactChip].
+     * Note that you can override it by applying Modifier.height directly on [CompactChip].
+     */
+    internal val CompactChipHeight = 32.dp
+
+    /**
+     * The default width applied for the [CompactChip] when it has no label provided.
+     * Note that you can override it by applying Modifier.width directly on [CompactChip].
+     */
+    internal val IconOnlyCompactChipWidth = 52.dp
+
+    /**
      * The default size of the icon when used inside a [Chip].
      */
     public val IconSize = 24.dp
@@ -420,7 +515,7 @@
         disabledContentColor: Color = contentColor.copy(alpha = ContentAlpha.disabled),
         disabledSecondaryContentColor: Color =
             secondaryContentColor.copy(alpha = ContentAlpha.disabled),
-        disabledIconTintColor: Color = disabledContentColor,
+        disabledIconTintColor: Color = iconTintColor.copy(alpha = ContentAlpha.disabled),
     ): ChipColors = DefaultChipColors(
         backgroundColor = backgroundColor,
         contentColor = contentColor,
diff --git a/wear/tiles/tiles/api/current.txt b/wear/tiles/tiles/api/current.txt
index 4209662..1d82943 100644
--- a/wear/tiles/tiles/api/current.txt
+++ b/wear/tiles/tiles/api/current.txt
@@ -124,6 +124,32 @@
     method public androidx.wear.tiles.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
   }
 
+  public final class DeviceParametersBuilders {
+    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters {
+    method public static androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder builder();
+    method public int getDevicePlatform();
+    method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+    method public int getScreenShape();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters build();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+  }
+
   public final class DimensionBuilders {
     method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
     method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
@@ -222,6 +248,41 @@
     method public androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp build();
   }
 
+  public final class EventBuilders {
+  }
+
+  public static final class EventBuilders.TileAddEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileAddEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileAddEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
+  }
+
+  public static final class EventBuilders.TileEnterEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileEnterEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
+  }
+
+  public static final class EventBuilders.TileLeaveEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileLeaveEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
+  }
+
+  public static final class EventBuilders.TileRemoveEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileRemoveEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
+  }
+
   public final class LayoutElementBuilders {
     field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
     field public static final int ARC_ANCHOR_END = 3; // 0x3
@@ -465,28 +526,28 @@
 
   public static class LayoutElementBuilders.FontStyles {
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
-    method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
   }
 
   public static final class LayoutElementBuilders.FontWeightProp {
@@ -870,6 +931,38 @@
     method public androidx.wear.tiles.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.tiles.ModifiersBuilders.Clickable.Builder);
   }
 
+  public final class RequestBuilders {
+  }
+
+  public static final class RequestBuilders.ResourcesRequest {
+    method public static androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder builder();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+    method public java.util.List<java.lang.String!> getResourceIds();
+    method public String getVersion();
+  }
+
+  public static final class RequestBuilders.ResourcesRequest.Builder {
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+  }
+
+  public static final class RequestBuilders.TileRequest {
+    method public static androidx.wear.tiles.RequestBuilders.TileRequest.Builder builder();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+    method public androidx.wear.tiles.StateBuilders.State? getState();
+  }
+
+  public static final class RequestBuilders.TileRequest.Builder {
+    method public androidx.wear.tiles.RequestBuilders.TileRequest build();
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State.Builder);
+  }
+
   public final class ResourceBuilders {
     field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
     field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
@@ -962,12 +1055,12 @@
     ctor public TileProviderService();
     method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
     method public android.os.IBinder? onBind(android.content.Intent);
-    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.readers.RequestReaders.ResourcesRequest);
-    method @MainThread protected void onTileAddEvent(androidx.wear.tiles.readers.EventReaders.TileAddEvent);
-    method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.readers.EventReaders.TileEnterEvent);
-    method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.readers.EventReaders.TileLeaveEvent);
-    method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.readers.EventReaders.TileRemoveEvent);
-    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.readers.RequestReaders.TileRequest);
+    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest);
+    method @MainThread protected void onTileAddEvent(androidx.wear.tiles.EventBuilders.TileAddEvent);
+    method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.EventBuilders.TileEnterEvent);
+    method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.EventBuilders.TileLeaveEvent);
+    method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.EventBuilders.TileRemoveEvent);
+    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest);
     field public static final String ACTION_BIND_TILE_PROVIDER = "androidx.wear.tiles.action.BIND_TILE_PROVIDER";
     field public static final String EXTRA_CLICKABLE_ID = "androidx.wear.tiles.extra.CLICKABLE_ID";
     field public static final String METADATA_PREVIEW_KEY = "androidx.wear.tiles.PREVIEW";
@@ -1062,52 +1155,3 @@
 
 }
 
-package androidx.wear.tiles.readers {
-
-  public class DeviceParametersReaders {
-    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
-    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
-    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
-    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
-    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
-  }
-
-  public static class DeviceParametersReaders.DeviceParameters {
-    method public int getDevicePlatform();
-    method @FloatRange(from=0.0, fromInclusive=false) public float getScreenDensity();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
-    method public int getScreenShape();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
-  }
-
-  public class EventReaders {
-  }
-
-  public static class EventReaders.TileAddEvent {
-  }
-
-  public static class EventReaders.TileEnterEvent {
-  }
-
-  public static class EventReaders.TileLeaveEvent {
-  }
-
-  public static class EventReaders.TileRemoveEvent {
-  }
-
-  public class RequestReaders {
-  }
-
-  public static class RequestReaders.ResourcesRequest {
-    method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
-    method public java.util.List<java.lang.String!> getResourceIds();
-    method public String getVersion();
-  }
-
-  public static class RequestReaders.TileRequest {
-    method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
-    method public androidx.wear.tiles.StateBuilders.State getState();
-  }
-
-}
-
diff --git a/wear/tiles/tiles/api/public_plus_experimental_current.txt b/wear/tiles/tiles/api/public_plus_experimental_current.txt
index 5688279..80d61ee 100644
--- a/wear/tiles/tiles/api/public_plus_experimental_current.txt
+++ b/wear/tiles/tiles/api/public_plus_experimental_current.txt
@@ -124,6 +124,32 @@
     method public androidx.wear.tiles.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
   }
 
+  public final class DeviceParametersBuilders {
+    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters {
+    method public static androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder builder();
+    method public int getDevicePlatform();
+    method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+    method public int getScreenShape();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters build();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+  }
+
   public final class DimensionBuilders {
     method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
     method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
@@ -222,6 +248,41 @@
     method public androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp build();
   }
 
+  public final class EventBuilders {
+  }
+
+  public static final class EventBuilders.TileAddEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileAddEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileAddEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
+  }
+
+  public static final class EventBuilders.TileEnterEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileEnterEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
+  }
+
+  public static final class EventBuilders.TileLeaveEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileLeaveEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
+  }
+
+  public static final class EventBuilders.TileRemoveEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileRemoveEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
+  }
+
   public final class LayoutElementBuilders {
     field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
     field public static final int ARC_ANCHOR_END = 3; // 0x3
@@ -469,28 +530,28 @@
 
   public static class LayoutElementBuilders.FontStyles {
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
-    method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
   }
 
   @androidx.wear.tiles.TilesExperimental public static final class LayoutElementBuilders.FontVariantProp {
@@ -887,6 +948,38 @@
     method public androidx.wear.tiles.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.tiles.ModifiersBuilders.Clickable.Builder);
   }
 
+  public final class RequestBuilders {
+  }
+
+  public static final class RequestBuilders.ResourcesRequest {
+    method public static androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder builder();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+    method public java.util.List<java.lang.String!> getResourceIds();
+    method public String getVersion();
+  }
+
+  public static final class RequestBuilders.ResourcesRequest.Builder {
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+  }
+
+  public static final class RequestBuilders.TileRequest {
+    method public static androidx.wear.tiles.RequestBuilders.TileRequest.Builder builder();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+    method public androidx.wear.tiles.StateBuilders.State? getState();
+  }
+
+  public static final class RequestBuilders.TileRequest.Builder {
+    method public androidx.wear.tiles.RequestBuilders.TileRequest build();
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State.Builder);
+  }
+
   public final class ResourceBuilders {
     field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
     field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
@@ -979,12 +1072,12 @@
     ctor public TileProviderService();
     method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
     method public android.os.IBinder? onBind(android.content.Intent);
-    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.readers.RequestReaders.ResourcesRequest);
-    method @MainThread protected void onTileAddEvent(androidx.wear.tiles.readers.EventReaders.TileAddEvent);
-    method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.readers.EventReaders.TileEnterEvent);
-    method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.readers.EventReaders.TileLeaveEvent);
-    method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.readers.EventReaders.TileRemoveEvent);
-    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.readers.RequestReaders.TileRequest);
+    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest);
+    method @MainThread protected void onTileAddEvent(androidx.wear.tiles.EventBuilders.TileAddEvent);
+    method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.EventBuilders.TileEnterEvent);
+    method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.EventBuilders.TileLeaveEvent);
+    method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.EventBuilders.TileRemoveEvent);
+    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest);
     field public static final String ACTION_BIND_TILE_PROVIDER = "androidx.wear.tiles.action.BIND_TILE_PROVIDER";
     field public static final String EXTRA_CLICKABLE_ID = "androidx.wear.tiles.extra.CLICKABLE_ID";
     field public static final String METADATA_PREVIEW_KEY = "androidx.wear.tiles.PREVIEW";
@@ -1082,52 +1175,3 @@
 
 }
 
-package androidx.wear.tiles.readers {
-
-  public class DeviceParametersReaders {
-    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
-    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
-    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
-    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
-    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
-  }
-
-  public static class DeviceParametersReaders.DeviceParameters {
-    method public int getDevicePlatform();
-    method @FloatRange(from=0.0, fromInclusive=false) public float getScreenDensity();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
-    method public int getScreenShape();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
-  }
-
-  public class EventReaders {
-  }
-
-  public static class EventReaders.TileAddEvent {
-  }
-
-  public static class EventReaders.TileEnterEvent {
-  }
-
-  public static class EventReaders.TileLeaveEvent {
-  }
-
-  public static class EventReaders.TileRemoveEvent {
-  }
-
-  public class RequestReaders {
-  }
-
-  public static class RequestReaders.ResourcesRequest {
-    method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
-    method public java.util.List<java.lang.String!> getResourceIds();
-    method public String getVersion();
-  }
-
-  public static class RequestReaders.TileRequest {
-    method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
-    method public androidx.wear.tiles.StateBuilders.State getState();
-  }
-
-}
-
diff --git a/wear/tiles/tiles/api/restricted_current.txt b/wear/tiles/tiles/api/restricted_current.txt
index 4209662..1d82943 100644
--- a/wear/tiles/tiles/api/restricted_current.txt
+++ b/wear/tiles/tiles/api/restricted_current.txt
@@ -124,6 +124,32 @@
     method public androidx.wear.tiles.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
   }
 
+  public final class DeviceParametersBuilders {
+    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters {
+    method public static androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder builder();
+    method public int getDevicePlatform();
+    method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+    method public int getScreenShape();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+  }
+
+  public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters build();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+  }
+
   public final class DimensionBuilders {
     method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
     method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
@@ -222,6 +248,41 @@
     method public androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp build();
   }
 
+  public final class EventBuilders {
+  }
+
+  public static final class EventBuilders.TileAddEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileAddEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileAddEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
+  }
+
+  public static final class EventBuilders.TileEnterEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileEnterEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
+  }
+
+  public static final class EventBuilders.TileLeaveEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileLeaveEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
+  }
+
+  public static final class EventBuilders.TileRemoveEvent {
+    method public static androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder builder();
+  }
+
+  public static final class EventBuilders.TileRemoveEvent.Builder {
+    method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
+  }
+
   public final class LayoutElementBuilders {
     field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
     field public static final int ARC_ANCHOR_END = 3; // 0x3
@@ -465,28 +526,28 @@
 
   public static class LayoutElementBuilders.FontStyles {
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
     method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3();
-    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
-    method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+    method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
   }
 
   public static final class LayoutElementBuilders.FontWeightProp {
@@ -870,6 +931,38 @@
     method public androidx.wear.tiles.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.tiles.ModifiersBuilders.Clickable.Builder);
   }
 
+  public final class RequestBuilders {
+  }
+
+  public static final class RequestBuilders.ResourcesRequest {
+    method public static androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder builder();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+    method public java.util.List<java.lang.String!> getResourceIds();
+    method public String getVersion();
+  }
+
+  public static final class RequestBuilders.ResourcesRequest.Builder {
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+    method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+  }
+
+  public static final class RequestBuilders.TileRequest {
+    method public static androidx.wear.tiles.RequestBuilders.TileRequest.Builder builder();
+    method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+    method public androidx.wear.tiles.StateBuilders.State? getState();
+  }
+
+  public static final class RequestBuilders.TileRequest.Builder {
+    method public androidx.wear.tiles.RequestBuilders.TileRequest build();
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
+    method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State.Builder);
+  }
+
   public final class ResourceBuilders {
     field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
     field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
@@ -962,12 +1055,12 @@
     ctor public TileProviderService();
     method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
     method public android.os.IBinder? onBind(android.content.Intent);
-    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.readers.RequestReaders.ResourcesRequest);
-    method @MainThread protected void onTileAddEvent(androidx.wear.tiles.readers.EventReaders.TileAddEvent);
-    method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.readers.EventReaders.TileEnterEvent);
-    method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.readers.EventReaders.TileLeaveEvent);
-    method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.readers.EventReaders.TileRemoveEvent);
-    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.readers.RequestReaders.TileRequest);
+    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest);
+    method @MainThread protected void onTileAddEvent(androidx.wear.tiles.EventBuilders.TileAddEvent);
+    method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.EventBuilders.TileEnterEvent);
+    method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.EventBuilders.TileLeaveEvent);
+    method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.EventBuilders.TileRemoveEvent);
+    method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest);
     field public static final String ACTION_BIND_TILE_PROVIDER = "androidx.wear.tiles.action.BIND_TILE_PROVIDER";
     field public static final String EXTRA_CLICKABLE_ID = "androidx.wear.tiles.extra.CLICKABLE_ID";
     field public static final String METADATA_PREVIEW_KEY = "androidx.wear.tiles.PREVIEW";
@@ -1062,52 +1155,3 @@
 
 }
 
-package androidx.wear.tiles.readers {
-
-  public class DeviceParametersReaders {
-    field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
-    field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
-    field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
-    field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
-    field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
-  }
-
-  public static class DeviceParametersReaders.DeviceParameters {
-    method public int getDevicePlatform();
-    method @FloatRange(from=0.0, fromInclusive=false) public float getScreenDensity();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
-    method public int getScreenShape();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
-  }
-
-  public class EventReaders {
-  }
-
-  public static class EventReaders.TileAddEvent {
-  }
-
-  public static class EventReaders.TileEnterEvent {
-  }
-
-  public static class EventReaders.TileLeaveEvent {
-  }
-
-  public static class EventReaders.TileRemoveEvent {
-  }
-
-  public class RequestReaders {
-  }
-
-  public static class RequestReaders.ResourcesRequest {
-    method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
-    method public java.util.List<java.lang.String!> getResourceIds();
-    method public String getVersion();
-  }
-
-  public static class RequestReaders.TileRequest {
-    method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
-    method public androidx.wear.tiles.StateBuilders.State getState();
-  }
-
-}
-
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
index ac7dd67..1aa8fbb 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
@@ -631,9 +631,10 @@
         }
 
         /**
-         * Gets the state to load the next tile with. This will be included in the TileRequest sent
-         * after this action is invoked by a {@link
-         * androidx.wear.tiles.ModifiersBuilders.Clickable}. Intended for testing purposes only.
+         * Gets the state to load the next tile with. This will be included in the {@link
+         * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by a
+         * {@link androidx.wear.tiles.ModifiersBuilders.Clickable}. Intended for testing purposes
+         * only.
          */
         @Nullable
         public State getRequestState() {
@@ -680,9 +681,9 @@
             Builder() {}
 
             /**
-             * Sets the state to load the next tile with. This will be included in the TileRequest
-             * sent after this action is invoked by a {@link
-             * androidx.wear.tiles.ModifiersBuilders.Clickable}.
+             * Sets the state to load the next tile with. This will be included in the {@link
+             * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by
+             * a {@link androidx.wear.tiles.ModifiersBuilders.Clickable}.
              */
             @NonNull
             public Builder setRequestState(@NonNull State requestState) {
@@ -691,9 +692,9 @@
             }
 
             /**
-             * Sets the state to load the next tile with. This will be included in the TileRequest
-             * sent after this action is invoked by a {@link
-             * androidx.wear.tiles.ModifiersBuilders.Clickable}.
+             * Sets the state to load the next tile with. This will be included in the {@link
+             * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by
+             * a {@link androidx.wear.tiles.ModifiersBuilders.Clickable}.
              */
             @NonNull
             public Builder setRequestState(@NonNull State.Builder requestStateBuilder) {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DeviceParametersBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DeviceParametersBuilders.java
new file mode 100644
index 0000000..624f512
--- /dev/null
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DeviceParametersBuilders.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2021 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.wear.tiles;
+
+import static androidx.annotation.Dimension.DP;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.tiles.proto.DeviceParametersProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Builders for request messages used to fetch tiles and resources. */
+public final class DeviceParametersBuilders {
+    private DeviceParametersBuilders() {}
+
+    /**
+     * The platform of the device requesting a tile.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @IntDef({DEVICE_PLATFORM_UNDEFINED, DEVICE_PLATFORM_WEAR_OS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DevicePlatform {}
+
+    /** Device platform is undefined. */
+    public static final int DEVICE_PLATFORM_UNDEFINED = 0;
+
+    /** Device is a Wear OS by Google device. */
+    public static final int DEVICE_PLATFORM_WEAR_OS = 1;
+
+    /**
+     * The shape of a screen.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @IntDef({SCREEN_SHAPE_UNDEFINED, SCREEN_SHAPE_ROUND, SCREEN_SHAPE_RECT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScreenShape {}
+
+    /** Screen shape is undefined. */
+    public static final int SCREEN_SHAPE_UNDEFINED = 0;
+
+    /** A round screen (typically found on most Wear devices). */
+    public static final int SCREEN_SHAPE_ROUND = 1;
+
+    /** Rectangular screens. */
+    public static final int SCREEN_SHAPE_RECT = 2;
+
+    /**
+     * Parameters describing the device requesting a tile update. This contains physical and logical
+     * characteristics about the device (e.g. screen size and density, etc).
+     */
+    public static final class DeviceParameters {
+        private final DeviceParametersProto.DeviceParameters mImpl;
+
+        private DeviceParameters(DeviceParametersProto.DeviceParameters impl) {
+            this.mImpl = impl;
+        }
+
+        /** Gets width of the device's screen in DP. */
+        @Dimension(unit = DP)
+        public int getScreenWidthDp() {
+            return mImpl.getScreenWidthDp();
+        }
+
+        /** Gets height of the device's screen in DP. */
+        @Dimension(unit = DP)
+        public int getScreenHeightDp() {
+            return mImpl.getScreenHeightDp();
+        }
+
+        /**
+         * Gets density of the display. This value is the scaling factor to get from DP to Pixels
+         * (px = dp * density).
+         */
+        @FloatRange(from = 0.0, fromInclusive = false, toInclusive = false)
+        public float getScreenDensity() {
+            return mImpl.getScreenDensity();
+        }
+
+        /** Gets the platform of the device. */
+        @DevicePlatform
+        public int getDevicePlatform() {
+            return mImpl.getDevicePlatform().getNumber();
+        }
+
+        /** Gets the shape of the device's screen. */
+        @ScreenShape
+        public int getScreenShape() {
+            return mImpl.getScreenShape().getNumber();
+        }
+
+        /** Returns a new {@link Builder}. */
+        @NonNull
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static DeviceParameters fromProto(
+                @NonNull DeviceParametersProto.DeviceParameters proto) {
+            return new DeviceParameters(proto);
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public DeviceParametersProto.DeviceParameters toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link DeviceParameters} */
+        public static final class Builder {
+            private final DeviceParametersProto.DeviceParameters.Builder mImpl =
+                    DeviceParametersProto.DeviceParameters.newBuilder();
+
+            Builder() {}
+
+            /** Sets width of the device's screen in DP. */
+            @NonNull
+            public Builder setScreenWidthDp(@Dimension(unit = DP) int screenWidthDp) {
+                mImpl.setScreenWidthDp(screenWidthDp);
+                return this;
+            }
+
+            /** Sets height of the device's screen in DP. */
+            @NonNull
+            public Builder setScreenHeightDp(@Dimension(unit = DP) int screenHeightDp) {
+                mImpl.setScreenHeightDp(screenHeightDp);
+                return this;
+            }
+
+            /**
+             * Sets density of the display. This value is the scaling factor to get from DP to
+             * Pixels (px = dp * density).
+             */
+            @NonNull
+            public Builder setScreenDensity(
+                    @FloatRange(from = 0.0, fromInclusive = false, toInclusive = false)
+                            float screenDensity) {
+                mImpl.setScreenDensity(screenDensity);
+                return this;
+            }
+
+            /** Sets the platform of the device. */
+            @NonNull
+            public Builder setDevicePlatform(@DevicePlatform int devicePlatform) {
+                mImpl.setDevicePlatform(
+                        DeviceParametersProto.DevicePlatform.forNumber(devicePlatform));
+                return this;
+            }
+
+            /** Sets the shape of the device's screen. */
+            @NonNull
+            public Builder setScreenShape(@ScreenShape int screenShape) {
+                mImpl.setScreenShape(DeviceParametersProto.ScreenShape.forNumber(screenShape));
+                return this;
+            }
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public DeviceParameters build() {
+                return DeviceParameters.fromProto(mImpl.build());
+            }
+        }
+    }
+}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
new file mode 100644
index 0000000..cfc85fd
--- /dev/null
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2021 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.wear.tiles;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.tiles.proto.EventProto;
+
+/** Builders for messages used when events happen in the Tiles system. */
+public final class EventBuilders {
+    private EventBuilders() {}
+
+    /** Event fired when a tile has been added to the carousel. */
+    public static final class TileAddEvent {
+        private final EventProto.TileAddEvent mImpl;
+
+        private TileAddEvent(EventProto.TileAddEvent impl) {
+            this.mImpl = impl;
+        }
+
+        /** Returns a new {@link Builder}. */
+        @NonNull
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static TileAddEvent fromProto(@NonNull EventProto.TileAddEvent proto) {
+            return new TileAddEvent(proto);
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public EventProto.TileAddEvent toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link TileAddEvent} */
+        public static final class Builder {
+            private final EventProto.TileAddEvent.Builder mImpl =
+                    EventProto.TileAddEvent.newBuilder();
+
+            Builder() {}
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public TileAddEvent build() {
+                return TileAddEvent.fromProto(mImpl.build());
+            }
+        }
+    }
+
+    /** Event fired when a tile has been removed from the carousel. */
+    public static final class TileRemoveEvent {
+        private final EventProto.TileRemoveEvent mImpl;
+
+        private TileRemoveEvent(EventProto.TileRemoveEvent impl) {
+            this.mImpl = impl;
+        }
+
+        /** Returns a new {@link Builder}. */
+        @NonNull
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static TileRemoveEvent fromProto(@NonNull EventProto.TileRemoveEvent proto) {
+            return new TileRemoveEvent(proto);
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public EventProto.TileRemoveEvent toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link TileRemoveEvent} */
+        public static final class Builder {
+            private final EventProto.TileRemoveEvent.Builder mImpl =
+                    EventProto.TileRemoveEvent.newBuilder();
+
+            Builder() {}
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public TileRemoveEvent build() {
+                return TileRemoveEvent.fromProto(mImpl.build());
+            }
+        }
+    }
+
+    /** Event fired when a tile is swiped to by the user (i.e. it's visible on screen). */
+    public static final class TileEnterEvent {
+        private final EventProto.TileEnterEvent mImpl;
+
+        private TileEnterEvent(EventProto.TileEnterEvent impl) {
+            this.mImpl = impl;
+        }
+
+        /** Returns a new {@link Builder}. */
+        @NonNull
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static TileEnterEvent fromProto(@NonNull EventProto.TileEnterEvent proto) {
+            return new TileEnterEvent(proto);
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public EventProto.TileEnterEvent toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link TileEnterEvent} */
+        public static final class Builder {
+            private final EventProto.TileEnterEvent.Builder mImpl =
+                    EventProto.TileEnterEvent.newBuilder();
+
+            Builder() {}
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public TileEnterEvent build() {
+                return TileEnterEvent.fromProto(mImpl.build());
+            }
+        }
+    }
+
+    /**
+     * Event fired when a tile is swiped away from by the user (i.e. it's no longer visible on
+     * screen).
+     */
+    public static final class TileLeaveEvent {
+        private final EventProto.TileLeaveEvent mImpl;
+
+        private TileLeaveEvent(EventProto.TileLeaveEvent impl) {
+            this.mImpl = impl;
+        }
+
+        /** Returns a new {@link Builder}. */
+        @NonNull
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static TileLeaveEvent fromProto(@NonNull EventProto.TileLeaveEvent proto) {
+            return new TileLeaveEvent(proto);
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public EventProto.TileLeaveEvent toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link TileLeaveEvent} */
+        public static final class Builder {
+            private final EventProto.TileLeaveEvent.Builder mImpl =
+                    EventProto.TileLeaveEvent.newBuilder();
+
+            Builder() {}
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public TileLeaveEvent build() {
+                return TileLeaveEvent.fromProto(mImpl.build());
+            }
+        }
+    }
+}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
index a07f141..5d76b9d 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
@@ -28,6 +28,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 import androidx.wear.tiles.ColorBuilders.ColorProp;
+import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
 import androidx.wear.tiles.DimensionBuilders.DegreesProp;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
@@ -43,7 +44,6 @@
 import androidx.wear.tiles.TypeBuilders.StringProp;
 import androidx.wear.tiles.proto.LayoutElementProto;
 import androidx.wear.tiles.proto.TypesProto;
-import androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java
new file mode 100644
index 0000000..f41fd416
--- /dev/null
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2021 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.wear.tiles;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.tiles.StateBuilders.State;
+import androidx.wear.tiles.proto.RequestProto;
+
+import java.util.List;
+
+/** Builders for request messages used to fetch tiles and resources. */
+public final class RequestBuilders {
+    private RequestBuilders() {}
+
+    /**
+     * Parameters passed to a {@link androidx.wear.tiles.TileBuilders.Tile} provider when the
+     * renderer is requesting a new version of the tile.
+     */
+    public static final class TileRequest {
+        private final RequestProto.TileRequest mImpl;
+
+        private TileRequest(RequestProto.TileRequest impl) {
+            this.mImpl = impl;
+        }
+
+        /** Gets parameters describing the device requesting the tile update. */
+        @Nullable
+        public DeviceParameters getDeviceParameters() {
+            if (mImpl.hasDeviceParameters()) {
+                return DeviceParameters.fromProto(mImpl.getDeviceParameters());
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets the state that should be used when building the tile. */
+        @Nullable
+        public State getState() {
+            if (mImpl.hasState()) {
+                return State.fromProto(mImpl.getState());
+            } else {
+                return null;
+            }
+        }
+
+        /** Returns a new {@link Builder}. */
+        @NonNull
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static TileRequest fromProto(@NonNull RequestProto.TileRequest proto) {
+            return new TileRequest(proto);
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public RequestProto.TileRequest toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link TileRequest} */
+        public static final class Builder {
+            private final RequestProto.TileRequest.Builder mImpl =
+                    RequestProto.TileRequest.newBuilder();
+
+            Builder() {}
+
+            /** Sets parameters describing the device requesting the tile update. */
+            @NonNull
+            public Builder setDeviceParameters(@NonNull DeviceParameters deviceParameters) {
+                mImpl.setDeviceParameters(deviceParameters.toProto());
+                return this;
+            }
+
+            /** Sets parameters describing the device requesting the tile update. */
+            @NonNull
+            public Builder setDeviceParameters(
+                    @NonNull DeviceParameters.Builder deviceParametersBuilder) {
+                mImpl.setDeviceParameters(deviceParametersBuilder.build().toProto());
+                return this;
+            }
+
+            /** Sets the state that should be used when building the tile. */
+            @NonNull
+            public Builder setState(@NonNull State state) {
+                mImpl.setState(state.toProto());
+                return this;
+            }
+
+            /** Sets the state that should be used when building the tile. */
+            @NonNull
+            public Builder setState(@NonNull State.Builder stateBuilder) {
+                mImpl.setState(stateBuilder.build().toProto());
+                return this;
+            }
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public TileRequest build() {
+                return TileRequest.fromProto(mImpl.build());
+            }
+        }
+    }
+
+    /**
+     * Parameters passed to a {@link androidx.wear.tiles.TileBuilders.Tile} provider when the
+     * renderer is requesting a specific resource version.
+     */
+    public static final class ResourcesRequest {
+        private final RequestProto.ResourcesRequest mImpl;
+
+        private ResourcesRequest(RequestProto.ResourcesRequest impl) {
+            this.mImpl = impl;
+        }
+
+        /**
+         * Gets the version of the resources being fetched. This is the same as the requested
+         * resource version, passed in {@link androidx.wear.tiles.TileBuilders.Tile}.
+         */
+        @NonNull
+        public String getVersion() {
+            return mImpl.getVersion();
+        }
+
+        /**
+         * Gets requested resource IDs. If not specified, all resources for the given version must
+         * be provided in the response.
+         */
+        @NonNull
+        public List<String> getResourceIds() {
+            return mImpl.getResourceIdsList();
+        }
+
+        /**
+         * Gets parameters describing the device requesting the resources. Intended for testing
+         * purposes only.
+         */
+        @Nullable
+        public DeviceParameters getDeviceParameters() {
+            if (mImpl.hasDeviceParameters()) {
+                return DeviceParameters.fromProto(mImpl.getDeviceParameters());
+            } else {
+                return null;
+            }
+        }
+
+        /** Returns a new {@link Builder}. */
+        @NonNull
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static ResourcesRequest fromProto(@NonNull RequestProto.ResourcesRequest proto) {
+            return new ResourcesRequest(proto);
+        }
+
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public RequestProto.ResourcesRequest toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link ResourcesRequest} */
+        public static final class Builder {
+            private final RequestProto.ResourcesRequest.Builder mImpl =
+                    RequestProto.ResourcesRequest.newBuilder();
+
+            Builder() {}
+
+            /**
+             * Sets the version of the resources being fetched. This is the same as the requested
+             * resource version, passed in {@link androidx.wear.tiles.TileBuilders.Tile}.
+             */
+            @NonNull
+            public Builder setVersion(@NonNull String version) {
+                mImpl.setVersion(version);
+                return this;
+            }
+
+            /**
+             * Adds one item to requested resource IDs. If not specified, all resources for the
+             * given version must be provided in the response.
+             */
+            @NonNull
+            public Builder addResourceId(@NonNull String resourceId) {
+                mImpl.addResourceIds(resourceId);
+                return this;
+            }
+
+            /** Sets parameters describing the device requesting the resources. */
+            @NonNull
+            public Builder setDeviceParameters(@NonNull DeviceParameters deviceParameters) {
+                mImpl.setDeviceParameters(deviceParameters.toProto());
+                return this;
+            }
+
+            /** Sets parameters describing the device requesting the resources. */
+            @NonNull
+            public Builder setDeviceParameters(
+                    @NonNull DeviceParameters.Builder deviceParametersBuilder) {
+                mImpl.setDeviceParameters(deviceParametersBuilder.build().toProto());
+                return this;
+            }
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public ResourcesRequest build() {
+                return ResourcesRequest.fromProto(mImpl.build());
+            }
+        }
+    }
+}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java
index 3b7700e..c8b8750 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java
@@ -365,8 +365,9 @@
          * resources.
          *
          * <p>This value must match the version of the resources required by the tile for the tile
-         * to render successfully, and must match the resource version specified in ResourcesRequest
-         * which triggered this request. Intended for testing purposes only.
+         * to render successfully, and must match the resource version specified in {@link
+         * androidx.wear.tiles.RequestBuilders.ResourcesRequest} which triggered this request.
+         * Intended for testing purposes only.
          */
         @NonNull
         public String getVersion() {
@@ -422,8 +423,8 @@
              * the resources.
              *
              * <p>This value must match the version of the resources required by the tile for the
-             * tile to render successfully, and must match the resource version specified in
-             * ResourcesRequest which triggered this request.
+             * tile to render successfully, and must match the resource version specified in {@link
+             * androidx.wear.tiles.RequestBuilders.ResourcesRequest} which triggered this request.
              */
             @NonNull
             public Builder setVersion(@NonNull String version) {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
index c1d54c8..628fc33 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
@@ -143,8 +143,7 @@
                         // we'll unbind, then immediately rebind. That said, this class should be
                         // used pretty rarely
                         // (and it'll be rare to have two in-flight update requests at once
-                        // regardless), so
-                        // it's probably fine.
+                        // regardless), so it's probably fine.
                         TileUpdateRequesterService updateRequesterService =
                                 TileUpdateRequesterService.Stub.asInterface(service);
 
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
index 1fcacd7..aef9ccf 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
@@ -41,9 +41,9 @@
 
         /**
          * Gets the resource version required for these tiles. This can be any developer-defined
-         * string; it is only used to cache resources, and is passed in ResourcesRequest if the
-         * system does not have a copy of the specified resource version. Intended for testing
-         * purposes only.
+         * string; it is only used to cache resources, and is passed in {@link
+         * androidx.wear.tiles.RequestBuilders.ResourcesRequest} if the system does not have a copy
+         * of the specified resource version. Intended for testing purposes only.
          */
         @NonNull
         public String getResourcesVersion() {
@@ -108,8 +108,9 @@
 
             /**
              * Sets the resource version required for these tiles. This can be any developer-defined
-             * string; it is only used to cache resources, and is passed in ResourcesRequest if the
-             * system does not have a copy of the specified resource version.
+             * string; it is only used to cache resources, and is passed in {@link
+             * androidx.wear.tiles.RequestBuilders.ResourcesRequest} if the system does not have a
+             * copy of the specified resource version.
              */
             @NonNull
             public Builder setResourcesVersion(@NonNull String resourcesVersion) {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java
index 6029f15..9dce43d 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java
@@ -27,16 +27,19 @@
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.wear.tiles.EventBuilders.TileAddEvent;
+import androidx.wear.tiles.EventBuilders.TileEnterEvent;
+import androidx.wear.tiles.EventBuilders.TileLeaveEvent;
+import androidx.wear.tiles.EventBuilders.TileRemoveEvent;
+import androidx.wear.tiles.RequestBuilders.ResourcesRequest;
+import androidx.wear.tiles.RequestBuilders.TileRequest;
 import androidx.wear.tiles.ResourceBuilders.Resources;
 import androidx.wear.tiles.TileBuilders.Tile;
 import androidx.wear.tiles.TileBuilders.Version;
+import androidx.wear.tiles.proto.EventProto;
+import androidx.wear.tiles.proto.RequestProto;
 import androidx.wear.tiles.proto.TileProto;
-import androidx.wear.tiles.readers.EventReaders.TileAddEvent;
-import androidx.wear.tiles.readers.EventReaders.TileEnterEvent;
-import androidx.wear.tiles.readers.EventReaders.TileLeaveEvent;
-import androidx.wear.tiles.readers.EventReaders.TileRemoveEvent;
-import androidx.wear.tiles.readers.RequestReaders.ResourcesRequest;
-import androidx.wear.tiles.readers.RequestReaders.TileRequest;
+import androidx.wear.tiles.protobuf.InvalidProtocolBufferException;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -208,10 +211,20 @@
                                 return;
                             }
 
-                            // TODO(b/166074385): Add tileId to TileRequest
+                            TileRequest tileRequest;
+
+                            try {
+                                tileRequest =
+                                        TileRequest.fromProto(
+                                                RequestProto.TileRequest.parseFrom(
+                                                        requestParams.getContents()));
+                            } catch (InvalidProtocolBufferException ex) {
+                                Log.e(TAG, "Error deserializing TileRequest payload.", ex);
+                                return;
+                            }
+
                             ListenableFuture<Tile> tileFuture =
-                                    tileProviderService.onTileRequest(
-                                            TileRequest.fromParcelable(requestParams, tileId));
+                                    tileProviderService.onTileRequest(tileRequest);
 
                             tileFuture.addListener(
                                     () -> {
@@ -256,10 +269,20 @@
                                 return;
                             }
 
-                            // TODO(b/166074385): Add tileId to ResourcesRequest
+                            ResourcesRequest req;
+
+                            try {
+                                req =
+                                        ResourcesRequest.fromProto(
+                                                RequestProto.ResourcesRequest.parseFrom(
+                                                        requestParams.getContents()));
+                            } catch (InvalidProtocolBufferException ex) {
+                                Log.e(TAG, "Error deserializing ResourcesRequest payload.", ex);
+                                return;
+                            }
+
                             ListenableFuture<Resources> resourcesFuture =
-                                    tileProviderService.onResourcesRequest(
-                                            ResourcesRequest.fromParcelable(requestParams, tileId));
+                                    tileProviderService.onResourcesRequest(req);
 
                             resourcesFuture.addListener(
                                     () -> {
@@ -301,7 +324,15 @@
                                 return;
                             }
 
-                            tileProviderService.onTileAddEvent(TileAddEvent.fromParcelable(data));
+                            try {
+                                TileAddEvent evt =
+                                        TileAddEvent.fromProto(
+                                                EventProto.TileAddEvent.parseFrom(
+                                                        data.getContents()));
+                                tileProviderService.onTileAddEvent(evt);
+                            } catch (InvalidProtocolBufferException ex) {
+                                Log.e(TAG, "Error deserializing TileAddEvent payload.", ex);
+                            }
                         }
                     });
         }
@@ -321,8 +352,15 @@
                                 return;
                             }
 
-                            tileProviderService.onTileRemoveEvent(
-                                    TileRemoveEvent.fromParcelable(data));
+                            try {
+                                TileRemoveEvent evt =
+                                        TileRemoveEvent.fromProto(
+                                                EventProto.TileRemoveEvent.parseFrom(
+                                                        data.getContents()));
+                                tileProviderService.onTileRemoveEvent(evt);
+                            } catch (InvalidProtocolBufferException ex) {
+                                Log.e(TAG, "Error deserializing TileRemoveEvent payload.", ex);
+                            }
                         }
                     });
         }
@@ -342,8 +380,15 @@
                                 return;
                             }
 
-                            tileProviderService.onTileEnterEvent(
-                                    TileEnterEvent.fromParcelable(data));
+                            try {
+                                TileEnterEvent evt =
+                                        TileEnterEvent.fromProto(
+                                                EventProto.TileEnterEvent.parseFrom(
+                                                        data.getContents()));
+                                tileProviderService.onTileEnterEvent(evt);
+                            } catch (InvalidProtocolBufferException ex) {
+                                Log.e(TAG, "Error deserializing TileEnterEvent payload.", ex);
+                            }
                         }
                     });
         }
@@ -363,8 +408,15 @@
                                 return;
                             }
 
-                            tileProviderService.onTileLeaveEvent(
-                                    TileLeaveEvent.fromParcelable(data));
+                            try {
+                                TileLeaveEvent evt =
+                                        TileLeaveEvent.fromProto(
+                                                EventProto.TileLeaveEvent.parseFrom(
+                                                        data.getContents()));
+                                tileProviderService.onTileLeaveEvent(evt);
+                            } catch (InvalidProtocolBufferException ex) {
+                                Log.e(TAG, "Error deserializing TileLeaveEvent payload.", ex);
+                            }
                         }
                     });
         }
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/DeviceParametersReaders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/DeviceParametersReaders.java
deleted file mode 100644
index 238d8e6..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/DeviceParametersReaders.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2020 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.wear.tiles.readers;
-
-import static androidx.annotation.Dimension.DP;
-
-import androidx.annotation.Dimension;
-import androidx.annotation.FloatRange;
-import androidx.annotation.IntDef;
-import androidx.annotation.RestrictTo;
-import androidx.wear.tiles.proto.DeviceParametersProto;
-import androidx.wear.tiles.readers.RequestReaders.TileRequest;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Readers for androidx.wear.tiles' device parameters structures. */
-public class DeviceParametersReaders {
-    private DeviceParametersReaders() {}
-
-    /**
-     * The platform of the device requesting a tile.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @IntDef({DEVICE_PLATFORM_UNDEFINED, DEVICE_PLATFORM_WEAR_OS})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DevicePlatform {}
-
-    /** Device platform is undefined. */
-    public static final int DEVICE_PLATFORM_UNDEFINED = 0;
-
-    /** Device is a Wear OS by Google device. */
-    public static final int DEVICE_PLATFORM_WEAR_OS = 1;
-
-    /**
-     * The shape of a screen.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @IntDef({SCREEN_SHAPE_UNDEFINED, SCREEN_SHAPE_ROUND, SCREEN_SHAPE_RECT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ScreenShape {}
-
-    /** Screen shape is undefined. */
-    public static final int SCREEN_SHAPE_UNDEFINED = 0;
-
-    /** A round screen (typically found on most Wear devices). */
-    public static final int SCREEN_SHAPE_ROUND = 1;
-
-    /** Rectangular screens. */
-    public static final int SCREEN_SHAPE_RECT = 2;
-
-    /** Reader for the Device Parameters returned from {@link TileRequest#getDeviceParameters()}. */
-    public static class DeviceParameters {
-        private final DeviceParametersProto.DeviceParameters mProto;
-
-        DeviceParameters(DeviceParametersProto.DeviceParameters proto) {
-            this.mProto = proto;
-        }
-
-        /** Get the width of the screen, in DP. */
-        @Dimension(unit = DP)
-        public int getScreenWidthDp() {
-            return mProto.getScreenWidthDp();
-        }
-
-        /** Get the height of the screen, in DP. */
-        @Dimension(unit = DP)
-        public int getScreenHeightDp() {
-            return mProto.getScreenHeightDp();
-        }
-
-        /**
-         * Get the density of the screen. This value is the scaling factor to get from DP to Pixels,
-         * where PX = DP * density.
-         */
-        @FloatRange(from = 0.0, fromInclusive = false)
-        public float getScreenDensity() {
-            return mProto.getScreenDensity();
-        }
-
-        /** Get the platform of the device. */
-        @DevicePlatform
-        public int getDevicePlatform() {
-            return mProto.getDevicePlatformValue();
-        }
-
-        /** Get the shape of the screen of the device. */
-        @ScreenShape
-        public int getScreenShape() {
-            return mProto.getScreenShapeValue();
-        }
-    }
-}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/EventReaders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/EventReaders.java
deleted file mode 100644
index 8300687..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/EventReaders.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2021 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.wear.tiles.readers;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
-import androidx.wear.tiles.TileAddEventData;
-import androidx.wear.tiles.TileEnterEventData;
-import androidx.wear.tiles.TileLeaveEventData;
-import androidx.wear.tiles.TileRemoveEventData;
-import androidx.wear.tiles.proto.EventProto;
-import androidx.wear.tiles.protobuf.ExtensionRegistryLite;
-import androidx.wear.tiles.protobuf.InvalidProtocolBufferException;
-
-/** Event readers for androidx.wear.tiles' Parcelable classes. */
-public class EventReaders {
-    private EventReaders() {}
-
-    /** Reader for Tile add event parameters. */
-    public static class TileAddEvent {
-        @SuppressWarnings("unused")
-        private final EventProto.TileAddEvent mProto;
-
-        private TileAddEvent(@NonNull EventProto.TileAddEvent proto) {
-            this.mProto = proto;
-        }
-
-        /**
-         * Create an instance of this reader from a given {@link TileAddEventData} instance.
-         *
-         * @hide
-         */
-        @RestrictTo(Scope.LIBRARY)
-        @NonNull
-        public static TileAddEvent fromParcelable(@NonNull TileAddEventData parcelable) {
-            try {
-                return new TileAddEvent(
-                        EventProto.TileAddEvent.parseFrom(
-                                parcelable.getContents(),
-                                ExtensionRegistryLite.getEmptyRegistry()));
-            } catch (InvalidProtocolBufferException ex) {
-                throw new IllegalArgumentException(
-                        "Passed TileAddEventData did not contain a valid proto payload", ex);
-            }
-        }
-    }
-
-    /** Reader for Tile remove event parameters. */
-    public static class TileRemoveEvent {
-        @SuppressWarnings("unused")
-        private final EventProto.TileRemoveEvent mProto;
-
-        private TileRemoveEvent(@NonNull EventProto.TileRemoveEvent proto) {
-            this.mProto = proto;
-        }
-
-        /**
-         * Create an instance of this reader from a given {@link TileRemoveEventData} instance.
-         *
-         * @hide
-         */
-        @RestrictTo(Scope.LIBRARY)
-        @NonNull
-        public static TileRemoveEvent fromParcelable(@NonNull TileRemoveEventData parcelable) {
-            try {
-                return new TileRemoveEvent(
-                        EventProto.TileRemoveEvent.parseFrom(
-                                parcelable.getContents(),
-                                ExtensionRegistryLite.getEmptyRegistry()));
-            } catch (InvalidProtocolBufferException ex) {
-                throw new IllegalArgumentException(
-                        "Passed TileRemoveEventData did not contain a valid proto payload", ex);
-            }
-        }
-    }
-
-    /** Reader for Tile enter event parameters. */
-    public static class TileEnterEvent {
-        @SuppressWarnings("unused")
-        private final EventProto.TileEnterEvent mProto;
-
-        private TileEnterEvent(@NonNull EventProto.TileEnterEvent proto) {
-            this.mProto = proto;
-        }
-
-        /**
-         * Create an instance of this reader from a given {@link TileEnterEventData} instance.
-         *
-         * @hide
-         */
-        @RestrictTo(Scope.LIBRARY)
-        @NonNull
-        public static TileEnterEvent fromParcelable(@NonNull TileEnterEventData parcelable) {
-            try {
-                return new TileEnterEvent(
-                        EventProto.TileEnterEvent.parseFrom(
-                                parcelable.getContents(),
-                                ExtensionRegistryLite.getEmptyRegistry()));
-            } catch (InvalidProtocolBufferException ex) {
-                throw new IllegalArgumentException(
-                        "Passed TileEnterEventData did not contain a valid proto payload", ex);
-            }
-        }
-    }
-
-    /** Reader for a Tile leave event parameters. */
-    public static class TileLeaveEvent {
-        @SuppressWarnings("unused")
-        private final EventProto.TileLeaveEvent mProto;
-
-        private TileLeaveEvent(@NonNull EventProto.TileLeaveEvent proto) {
-            this.mProto = proto;
-        }
-
-        /**
-         * Create an instance of this reader from a given {@link TileLeaveEventData} instance.
-         *
-         * @hide
-         */
-        @RestrictTo(Scope.LIBRARY)
-        @NonNull
-        public static TileLeaveEvent fromParcelable(@NonNull TileLeaveEventData parcelable) {
-            try {
-                return new TileLeaveEvent(
-                        EventProto.TileLeaveEvent.parseFrom(
-                                parcelable.getContents(),
-                                ExtensionRegistryLite.getEmptyRegistry()));
-            } catch (InvalidProtocolBufferException ex) {
-                throw new IllegalArgumentException(
-                        "Passed TileLeaveEventData did not contain a valid proto payload", ex);
-            }
-        }
-    }
-}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/RequestReaders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/RequestReaders.java
deleted file mode 100644
index 4462123..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/RequestReaders.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2020 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.wear.tiles.readers;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
-import androidx.wear.tiles.ResourcesRequestData;
-import androidx.wear.tiles.StateBuilders.State;
-import androidx.wear.tiles.TileRequestData;
-import androidx.wear.tiles.proto.RequestProto;
-import androidx.wear.tiles.protobuf.ExtensionRegistryLite;
-import androidx.wear.tiles.protobuf.InvalidProtocolBufferException;
-import androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters;
-
-import java.util.List;
-
-/** Request readers for androidx.wear.tiles' Parcelable classes. */
-public class RequestReaders {
-    private RequestReaders() {}
-
-    /** Reader for Tile request parameters. */
-    public static class TileRequest {
-        private final RequestProto.TileRequest mProto;
-
-        @SuppressWarnings("unused")
-        private final int mTileId;
-
-        private TileRequest(RequestProto.TileRequest proto, int tileId) {
-            this.mProto = proto;
-            this.mTileId = tileId;
-        }
-
-        /** Get the {@link State} that the tile should be built with. */
-        @NonNull
-        public State getState() {
-            return State.fromProto(mProto.getState());
-        }
-
-        /** Get parameters describing the device requesting this tile. */
-        @NonNull
-        public DeviceParameters getDeviceParameters() {
-            return new DeviceParameters(mProto.getDeviceParameters());
-        }
-
-        /** @hide */
-        @RestrictTo(Scope.LIBRARY)
-        @NonNull
-        public static TileRequest fromParcelable(@NonNull TileRequestData parcelable, int tileId) {
-            try {
-                return new TileRequest(
-                        RequestProto.TileRequest.parseFrom(
-                                parcelable.getContents(), ExtensionRegistryLite.getEmptyRegistry()),
-                        tileId);
-            } catch (InvalidProtocolBufferException ex) {
-                throw new IllegalArgumentException(
-                        "Passed TileRequestData did not contain a valid proto payload", ex);
-            }
-        }
-    }
-
-    /** Reader for resource request parameters. */
-    public static class ResourcesRequest {
-        private final RequestProto.ResourcesRequest mProto;
-
-        @SuppressWarnings("unused")
-        private final int mTileId;
-
-        private ResourcesRequest(@NonNull RequestProto.ResourcesRequest proto, int tileId) {
-            this.mProto = proto;
-            this.mTileId = tileId;
-        }
-
-        /** @hide */
-        @RestrictTo(Scope.LIBRARY)
-        @NonNull
-        public static ResourcesRequest fromParcelable(
-                @NonNull ResourcesRequestData parcelable, int tileId) {
-            try {
-                return new ResourcesRequest(
-                        RequestProto.ResourcesRequest.parseFrom(
-                                parcelable.getContents(), ExtensionRegistryLite.getEmptyRegistry()),
-                        tileId);
-            } catch (InvalidProtocolBufferException ex) {
-                throw new IllegalArgumentException(
-                        "Passed ResourcesRequestData did not contain a valid proto payload", ex);
-            }
-        }
-
-        /** Get the requested resource version. */
-        @NonNull
-        public String getVersion() {
-            return mProto.getVersion();
-        }
-
-        /**
-         * Get the requested resource IDs. May be empty, in which case all resources should be
-         * returned.
-         */
-        @NonNull
-        public List<String> getResourceIds() {
-            return mProto.getResourceIdsList();
-        }
-
-        /** Get parameters describing the device requesting these resources. */
-        @NonNull
-        public DeviceParameters getDeviceParameters() {
-            return new DeviceParameters(mProto.getDeviceParameters());
-        }
-    }
-}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/package-info.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/package-info.java
deleted file mode 100644
index 48deb88..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-/**
- * Contains {@link androidx.wear.tiles.readers.RequestReaders.TileRequest} and {@link
- * androidx.wear.tiles.readers.RequestReaders.ResourcesRequest}, which are passed as parameters to
- * {@link androidx.wear.tiles.TileProviderService#onTileRequest} and {@link
- * androidx.wear.tiles.TileProviderService#onResourcesRequest} respectively.
- */
-package androidx.wear.tiles.readers;
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
index 286fc1f..4f00c4f 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
@@ -24,7 +24,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.concurrent.futures.ResolvableFuture;
-import androidx.wear.tiles.readers.RequestReaders;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -75,7 +74,7 @@
         @NonNull
         @Override
         protected ListenableFuture<TileBuilders.Tile> onTileRequest(
-                @NonNull RequestReaders.TileRequest requestParams) {
+                @NonNull RequestBuilders.TileRequest requestParams) {
             ResolvableFuture<TileBuilders.Tile> f = ResolvableFuture.create();
             f.set(null);
             return f;
@@ -84,7 +83,7 @@
         @NonNull
         @Override
         protected ListenableFuture<ResourceBuilders.Resources> onResourcesRequest(
-                @NonNull RequestReaders.ResourcesRequest requestParams) {
+                @NonNull RequestBuilders.ResourcesRequest requestParams) {
             ResolvableFuture<ResourceBuilders.Resources> f = ResolvableFuture.create();
             f.set(null);
             return f;
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
index 0a3e8f7..cdcd478 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
@@ -25,17 +25,17 @@
 import android.os.Looper;
 
 import androidx.annotation.NonNull;
+import androidx.wear.tiles.EventBuilders.TileAddEvent;
+import androidx.wear.tiles.EventBuilders.TileEnterEvent;
+import androidx.wear.tiles.EventBuilders.TileLeaveEvent;
+import androidx.wear.tiles.EventBuilders.TileRemoveEvent;
+import androidx.wear.tiles.RequestBuilders.ResourcesRequest;
+import androidx.wear.tiles.RequestBuilders.TileRequest;
 import androidx.wear.tiles.TileBuilders.Version;
 import androidx.wear.tiles.proto.EventProto;
 import androidx.wear.tiles.proto.RequestProto;
 import androidx.wear.tiles.proto.ResourceProto.Resources;
 import androidx.wear.tiles.proto.TileProto.Tile;
-import androidx.wear.tiles.readers.EventReaders.TileAddEvent;
-import androidx.wear.tiles.readers.EventReaders.TileEnterEvent;
-import androidx.wear.tiles.readers.EventReaders.TileLeaveEvent;
-import androidx.wear.tiles.readers.EventReaders.TileRemoveEvent;
-import androidx.wear.tiles.readers.RequestReaders.ResourcesRequest;
-import androidx.wear.tiles.readers.RequestReaders.TileRequest;
 
 import com.google.common.truth.Expect;
 import com.google.common.util.concurrent.Futures;
diff --git a/wear/wear-complications-data/api/restricted_current.txt b/wear/wear-complications-data/api/restricted_current.txt
index 4b1188f..77a59ce 100644
--- a/wear/wear-complications-data/api/restricted_current.txt
+++ b/wear/wear-complications-data/api/restricted_current.txt
@@ -1,5 +1,5 @@
 // Signature format: 4.0
-package android.support.wearable.complications {
+package @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) android.support.wearable.complications {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class ComplicationData implements android.os.Parcelable {
     method public int describeContents();
diff --git a/wear/wear-complications-data/src/main/java/android/support/wearable/complications/package-info.java b/wear/wear-complications-data/src/main/java/android/support/wearable/complications/package-info.java
new file mode 100644
index 0000000..7aa7905
--- /dev/null
+++ b/wear/wear-complications-data/src/main/java/android/support/wearable/complications/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 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.
+ */
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+package android.support.wearable.complications;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import androidx.annotation.RestrictTo;
diff --git a/wear/wear-ongoing/api/current.txt b/wear/wear-ongoing/api/current.txt
index edd5ae4..9eb1123 100644
--- a/wear/wear-ongoing/api/current.txt
+++ b/wear/wear-ongoing/api/current.txt
@@ -12,6 +12,7 @@
     method public androidx.wear.ongoing.Status? getStatus();
     method public String? getTag();
     method public long getTimestamp();
+    method public String? getTitle();
     method public android.app.PendingIntent getTouchIntent();
     method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivity!>);
     method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context);
@@ -31,6 +32,7 @@
     method public androidx.wear.ongoing.OngoingActivity.Builder setStaticIcon(android.graphics.drawable.Icon);
     method public androidx.wear.ongoing.OngoingActivity.Builder setStaticIcon(@DrawableRes int);
     method public androidx.wear.ongoing.OngoingActivity.Builder setStatus(androidx.wear.ongoing.Status);
+    method public androidx.wear.ongoing.OngoingActivity.Builder setTitle(String);
     method public androidx.wear.ongoing.OngoingActivity.Builder setTouchIntent(android.app.PendingIntent);
   }
 
diff --git a/wear/wear-ongoing/api/public_plus_experimental_current.txt b/wear/wear-ongoing/api/public_plus_experimental_current.txt
index edd5ae4..9eb1123 100644
--- a/wear/wear-ongoing/api/public_plus_experimental_current.txt
+++ b/wear/wear-ongoing/api/public_plus_experimental_current.txt
@@ -12,6 +12,7 @@
     method public androidx.wear.ongoing.Status? getStatus();
     method public String? getTag();
     method public long getTimestamp();
+    method public String? getTitle();
     method public android.app.PendingIntent getTouchIntent();
     method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivity!>);
     method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context);
@@ -31,6 +32,7 @@
     method public androidx.wear.ongoing.OngoingActivity.Builder setStaticIcon(android.graphics.drawable.Icon);
     method public androidx.wear.ongoing.OngoingActivity.Builder setStaticIcon(@DrawableRes int);
     method public androidx.wear.ongoing.OngoingActivity.Builder setStatus(androidx.wear.ongoing.Status);
+    method public androidx.wear.ongoing.OngoingActivity.Builder setTitle(String);
     method public androidx.wear.ongoing.OngoingActivity.Builder setTouchIntent(android.app.PendingIntent);
   }
 
diff --git a/wear/wear-ongoing/api/restricted_current.txt b/wear/wear-ongoing/api/restricted_current.txt
index edd5ae4..9eb1123 100644
--- a/wear/wear-ongoing/api/restricted_current.txt
+++ b/wear/wear-ongoing/api/restricted_current.txt
@@ -12,6 +12,7 @@
     method public androidx.wear.ongoing.Status? getStatus();
     method public String? getTag();
     method public long getTimestamp();
+    method public String? getTitle();
     method public android.app.PendingIntent getTouchIntent();
     method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivity!>);
     method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context);
@@ -31,6 +32,7 @@
     method public androidx.wear.ongoing.OngoingActivity.Builder setStaticIcon(android.graphics.drawable.Icon);
     method public androidx.wear.ongoing.OngoingActivity.Builder setStaticIcon(@DrawableRes int);
     method public androidx.wear.ongoing.OngoingActivity.Builder setStatus(androidx.wear.ongoing.Status);
+    method public androidx.wear.ongoing.OngoingActivity.Builder setTitle(String);
     method public androidx.wear.ongoing.OngoingActivity.Builder setTouchIntent(android.app.PendingIntent);
   }
 
diff --git a/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivity.java b/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivity.java
index c51d447..dd65d05 100644
--- a/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivity.java
+++ b/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivity.java
@@ -106,6 +106,7 @@
         private LocusIdCompat mLocusId;
         private int mOngoingActivityId = DEFAULT_ID;
         private String mCategory;
+        private String mTitle;
 
         static final int DEFAULT_ID = -1;
 
@@ -242,6 +243,16 @@
         }
 
         /**
+         * Sets the Title of this {@link OngoingActivity}, this could be used by the launcher to
+         * override the app's title.
+         */
+        @NonNull
+        public Builder setTitle(@NonNull String title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
          * Combine all options provided and the information in the notification if needed,
          * return a new {@link OngoingActivity} object.
          *
@@ -288,7 +299,8 @@
                         locusId == null ? null : locusId.getId(),
                         mOngoingActivityId,
                         category,
-                        SystemClock.elapsedRealtime()
+                        SystemClock.elapsedRealtime(),
+                        mTitle
                     ));
         }
     }
@@ -384,6 +396,14 @@
     }
 
     /**
+     * Get the title of this {@link OngoingActivity} if set.
+     */
+    @Nullable
+    public String getTitle() {
+        return mData.getTitle();
+    }
+
+    /**
      * Notify the system that this activity should be shown as an Ongoing Activity.
      *
      * This will modify the notification builder associated with this Ongoing Activity, so needs
diff --git a/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivityData.java b/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivityData.java
index 9e06859..75bc162 100644
--- a/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivityData.java
+++ b/wear/wear-ongoing/src/main/java/androidx/wear/ongoing/OngoingActivityData.java
@@ -17,7 +17,6 @@
 
 import android.app.PendingIntent;
 import android.graphics.drawable.Icon;
-import android.os.SystemClock;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -65,6 +64,10 @@
     @ParcelField(value = 8)
     long mTimestamp;
 
+    @Nullable
+    @ParcelField(value = 9, defaultValue = "null")
+    String mTitle;
+
     // Required by VersionedParcelable
     OngoingActivityData() {
     }
@@ -77,7 +80,8 @@
             @Nullable String locusId,
             int ongoingActivityId,
             @Nullable String category,
-            long timestamp
+            long timestamp,
+            @Nullable String title
     ) {
         mAnimatedIcon = animatedIcon;
         mStaticIcon = staticIcon;
@@ -87,81 +91,52 @@
         mOngoingActivityId = ongoingActivityId;
         mCategory = category;
         mTimestamp = timestamp;
+        mTitle = title;
     }
 
-    /**
-     * Get the animated icon that can be used on some surfaces to represent this
-     * {@link OngoingActivity}. For example, in the WatchFace.
-     */
     @Nullable
-    public Icon getAnimatedIcon() {
+    Icon getAnimatedIcon() {
         return mAnimatedIcon;
     }
 
-    /**
-     * Get the static icon that can be used on some surfaces to represent this
-     * {@link OngoingActivity}. For example in the WatchFace in ambient mode. If not set, returns
-     *  the small icon of the corresponding Notification.
-     */
     @NonNull
-    public Icon getStaticIcon() {
+    Icon getStaticIcon() {
         return mStaticIcon;
     }
 
-    /**
-     * Get the status of this ongoing activity, the status may be displayed on the UI to
-     * show progress of the Ongoing Activity. If not set, returns the content text of the
-     * corresponding Notification.
-     */
     @Nullable
-    public OngoingActivityStatus getStatus() {
+    OngoingActivityStatus getStatus() {
         return mStatus;
     }
 
-    /**
-     * Get the intent to be used to go back to the activity when the user interacts with the
-     * Ongoing Activity in other surfaces (for example, taps the Icon on the WatchFace). If not
-     * set, returns the touch intent of the corresponding Notification.
-     */
     @NonNull
-    public PendingIntent getTouchIntent() {
+    PendingIntent getTouchIntent() {
         return mTouchIntent;
     }
 
-    /**
-     * Get the LocusId of this {@link OngoingActivity}, this can be used by the launcher to
-     * identify the corresponding launcher item and display it accordingly. If not set, returns
-     * the one in the corresponding Notification.
-     */
     @Nullable
-    public LocusIdCompat getLocusId() {
+    LocusIdCompat getLocusId() {
         return mLocusId == null ? null : new LocusIdCompat(mLocusId);
     }
 
-    /**
-     * Give the id to this {@link OngoingActivity}, as a way to reference it in
-     * [fromExistingOngoingActivity]
-     */
-    public int getOngoingActivityId() {
+    int getOngoingActivityId() {
         return mOngoingActivityId;
     }
 
-    /**
-     * Get the Category of this {@link OngoingActivity} if set, otherwise the category of the
-     * corresponding notification.
-     */
     @Nullable
-    public String getCategory() {
+    String getCategory() {
         return mCategory;
     }
 
-    /**
-     * Get the time (in {@link SystemClock#elapsedRealtime()} time) the OngoingActivity was built.
-     */
-    public long getTimestamp() {
+    long getTimestamp() {
         return mTimestamp;
     }
 
+    @Nullable
+    String getTitle() {
+        return mTitle;
+    }
+
     // Status is mutable, by the library.
     void setStatus(@NonNull OngoingActivityStatus status) {
         mStatus = status;
diff --git a/wear/wear-ongoing/src/test/java/androidx/wear/ongoing/OngoingActivityTest.kt b/wear/wear-ongoing/src/test/java/androidx/wear/ongoing/OngoingActivityTest.kt
index 1519bf6..93290b7 100644
--- a/wear/wear-ongoing/src/test/java/androidx/wear/ongoing/OngoingActivityTest.kt
+++ b/wear/wear-ongoing/src/test/java/androidx/wear/ongoing/OngoingActivityTest.kt
@@ -34,6 +34,7 @@
     )
     private val NotificationId = 4321
     private val ChannelId = "ChannelId"
+    private val Title = "AppTitle"
 
     private lateinit var context: Context
     private lateinit var notificationManager: NotificationManager
@@ -78,6 +79,7 @@
             .setOngoingActivityId(OaId)
             .setStatus(BasicStatus)
             .setTouchIntent(PendingIntentValue)
+            .setTitle(Title)
             .build()
         oa.apply(context)
 
@@ -91,6 +93,7 @@
         assertEquals(OaId, received.ongoingActivityId)
         // TODO(ssancho): check status
         assertEquals(PendingIntentValue, received.touchIntent)
+        assertEquals(Title, received.title)
     }
 
     @Test
@@ -103,6 +106,7 @@
             .setOngoingActivityId(OaId)
             .setStatus(BasicStatus)
             .setTouchIntent(PendingIntentValue)
+            .setTitle(Title)
             .build()
         oa.apply(context)
         notificationManager.notify(NotificationId, builder.build())
@@ -126,6 +130,7 @@
         assertEquals(OaId, received.ongoingActivityId)
         // TODO(ssancho): check status
         assertEquals(PendingIntentValue, received.touchIntent)
+        assertEquals(Title, received.title)
 
         notificationManager.cancel(NotificationId)
     }
@@ -193,6 +198,7 @@
             .setOngoingActivityId(OaId)
             .setStatus(BasicStatus)
             .setTouchIntent(PendingIntentValue)
+            .setTitle(Title)
             .build()
         oa.apply(context)
         val notification = builder.build()
@@ -209,6 +215,7 @@
         assertEquals(OaId, received.ongoingActivityId)
         // TODO(ssancho): check status
         assertEquals(PendingIntentValue, received.touchIntent)
+        assertEquals(Title, received.title)
     }
 
     @Test
diff --git a/wear/wear-watchface-data/api/restricted_current.txt b/wear/wear-watchface-data/api/restricted_current.txt
index 58265360..12e7670 100644
--- a/wear/wear-watchface-data/api/restricted_current.txt
+++ b/wear/wear-watchface-data/api/restricted_current.txt
@@ -1,5 +1,5 @@
 // Signature format: 4.0
-package android.support.wearable.watchface {
+package @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) android.support.wearable.watchface {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class Constants {
     field public static final String ACTION_REQUEST_STATE = "com.google.android.wearable.watchfaces.action.REQUEST_STATE";
@@ -93,7 +93,7 @@
 
 }
 
-package android.support.wearable.watchface.accessibility {
+package @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) android.support.wearable.watchface.accessibility {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class AccessibilityUtils {
     method public static android.support.wearable.complications.TimeDependentText generateContentDescription(android.content.Context, android.support.wearable.complications.ComplicationData);
diff --git a/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/accessibility/package-info.java b/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/accessibility/package-info.java
new file mode 100644
index 0000000..45c6efc
--- /dev/null
+++ b/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/accessibility/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 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.
+ */
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+package android.support.wearable.watchface.accessibility;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import androidx.annotation.RestrictTo;
diff --git a/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/package-info.java b/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/package-info.java
new file mode 100644
index 0000000..4b57c76
--- /dev/null
+++ b/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 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.
+ */
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+package android.support.wearable.watchface;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import androidx.annotation.RestrictTo;
diff --git a/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditingSessionTest.kt b/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditingSessionTest.kt
index ef2ef3f..fe73ba9 100644
--- a/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditingSessionTest.kt
+++ b/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditingSessionTest.kt
@@ -1110,6 +1110,9 @@
         val editorObserver = TestEditorObserver()
         val observerId = EditorService.globalEditorService.registerObserver(editorObserver)
 
+        val oldWFColorStyleSetting = editorDelegate.userStyle[colorStyleSetting]!!.id.value
+        val oldWFWatchHandStyleSetting = editorDelegate.userStyle[watchHandStyleSetting]!!.id.value
+
         scenario.onActivity { activity ->
             runBlocking {
                 // Select [blueStyleOption] and [gothicStyleOption].
@@ -1135,11 +1138,12 @@
         assertThat(result.watchFaceId.id).isEqualTo(testInstanceId.id)
         assertTrue(result.shouldCommitChanges)
 
-        // The style change should also have been applied to the watchface
+        // The style change shouldn't be applied to the watchface as it gets reverted to the old
+        // one when editor closes.
         assertThat(editorDelegate.userStyle[colorStyleSetting]!!.id.value)
-            .isEqualTo(blueStyleOption.id.value)
+            .isEqualTo(oldWFColorStyleSetting)
         assertThat(editorDelegate.userStyle[watchHandStyleSetting]!!.id.value)
-            .isEqualTo(gothicStyleOption.id.value)
+            .isEqualTo(oldWFWatchHandStyleSetting)
 
         assertThat(result.previewComplicationsData.size).isEqualTo(2)
         val leftComplicationData = result.previewComplicationsData[LEFT_COMPLICATION_ID] as
@@ -1514,7 +1518,8 @@
                 mockWatchFaceHostApi,
                 watchState,
                 currentUserStyleRepository,
-                ComplicationsManager(emptyList(), currentUserStyleRepository)
+                ComplicationsManager(emptyList(), currentUserStyleRepository),
+                Calendar.getInstance()
             )
 
             assertThat(activity.onCreateException).isInstanceOf(IllegalStateException::class.java)
diff --git a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
index c974e38..eef4f27 100644
--- a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
+++ b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
@@ -77,7 +77,10 @@
  * Interface for manipulating watch face state during an editing session for a watch face editing
  * session. The editor should adjust [userStyle] and call [openComplicationProviderChooser] to
  * configure the watch face and call [close] when done. This reports the updated [EditorState] to
- * the [EditorListener]s registered via [EditorServiceClient.addListener].
+ * the [EditorListener]s registered via [EditorServiceClient.addListener]. Style changes applied
+ * during the editor session are temporary and will be reverted when the editor session completes.
+ * In the event that the editor sessions results in a new watch face configuration that will be
+ * subsequently reapplied when the new configuration is provided by the system.
  */
 public abstract class EditorSession : AutoCloseable {
     /** The [ComponentName] of the watch face being edited. */
@@ -91,7 +94,8 @@
     @get:RequiresApi(Build.VERSION_CODES.R)
     public abstract val watchFaceId: WatchFaceId
 
-    /** The current [UserStyle]. Assigning to this will cause the style to update. */
+    /** The current [UserStyle]. Assigning to this will cause the style to update. However, styling
+     * changes to the watch face will be reverted upon exit. */
     public abstract var userStyle: UserStyle
 
     /** The UTC reference preview time for this watch face in milliseconds since the epoch. */
@@ -113,10 +117,10 @@
      * because there are circumstances where [ComponentActivity.onStop] doesn't get called but the
      * UX requires us to commit changes.
      *
-     * If false upon exit for an on watch face editor, the original UserStyle is restored. Note we
-     * need SysUI's help to revert any complication provider changes. Caveat some providers have
-     * their own config (e.g. the world clock has a timezone setting) and that config currently
-     * can't be reverted.
+     * Regardless of the value, on completion of the editor session, the original UserStyle is
+     * restored. Note we need SysUI's help to revert any complication provider changes. Caveat
+     * some providers have their own config (e.g. the world clock has a timezone setting) and
+     * that config currently can't be reverted.
      */
     @get:UiThread
     @get:JvmName("isCommitChangesOnClose")
@@ -665,8 +669,11 @@
         if (this::editorDelegate.isInitialized) {
             editorDelegate.onDestroy()
         }
-        // Revert any changes to the UserStyle if needed.
-        if (!commitChangesOnClose && this::previousWatchFaceUserStyle.isInitialized) {
+        // Revert any changes to the user style that was set during the editing session. The
+        // system will update the user style and communicate it to the active watch face if
+        // needed. This guarantees that the system is always the source of truth for the current
+        // style.
+        if (this::previousWatchFaceUserStyle.isInitialized) {
             userStyle = previousWatchFaceUserStyle
         }
     }
diff --git a/wear/wear-watchface/api/current.txt b/wear/wear-watchface/api/current.txt
index 53b8d81..3e4c282 100644
--- a/wear/wear-watchface/api/current.txt
+++ b/wear/wear-watchface/api/current.txt
@@ -21,7 +21,7 @@
   }
 
   public interface CanvasComplicationFactory {
-    method @UiThread public androidx.wear.watchface.CanvasComplication create(androidx.wear.watchface.WatchState, androidx.wear.watchface.CanvasComplication.InvalidateCallback);
+    method @WorkerThread public androidx.wear.watchface.CanvasComplication create(androidx.wear.watchface.WatchState, androidx.wear.watchface.CanvasComplication.InvalidateCallback);
   }
 
   public final class Complication {
@@ -121,14 +121,7 @@
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class MutableObservableWatchData<T> extends androidx.wear.watchface.ObservableWatchData<T> {
-    ctor public MutableObservableWatchData(T? initialValue);
-    ctor public MutableObservableWatchData();
-    method @UiThread public void setValue(T v);
-    property @UiThread public T value;
-  }
-
-  public class ObservableWatchData<T> {
+  public abstract sealed class ObservableWatchData<T> {
     method @UiThread public final void addObserver(androidx.wear.watchface.Observer<T> observer);
     method @UiThread public T getValue();
     method @UiThread public final T getValueOr(T p);
@@ -138,6 +131,13 @@
     property @UiThread public T value;
   }
 
+  public static final class ObservableWatchData.MutableObservableWatchData<T> extends androidx.wear.watchface.ObservableWatchData<T> {
+    ctor public ObservableWatchData.MutableObservableWatchData(T? initialValue);
+    ctor public ObservableWatchData.MutableObservableWatchData();
+    method @UiThread public void setValue(T v);
+    property @UiThread public T value;
+  }
+
   public interface Observer<T> {
     method public void onChanged(T);
   }
diff --git a/wear/wear-watchface/api/public_plus_experimental_current.txt b/wear/wear-watchface/api/public_plus_experimental_current.txt
index 0049d9a..b12e928 100644
--- a/wear/wear-watchface/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface/api/public_plus_experimental_current.txt
@@ -21,7 +21,7 @@
   }
 
   public interface CanvasComplicationFactory {
-    method @UiThread public androidx.wear.watchface.CanvasComplication create(androidx.wear.watchface.WatchState, androidx.wear.watchface.CanvasComplication.InvalidateCallback);
+    method @WorkerThread public androidx.wear.watchface.CanvasComplication create(androidx.wear.watchface.WatchState, androidx.wear.watchface.CanvasComplication.InvalidateCallback);
   }
 
   public final class Complication {
@@ -121,14 +121,7 @@
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class MutableObservableWatchData<T> extends androidx.wear.watchface.ObservableWatchData<T> {
-    ctor public MutableObservableWatchData(T? initialValue);
-    ctor public MutableObservableWatchData();
-    method @UiThread public void setValue(T v);
-    property @UiThread public T value;
-  }
-
-  public class ObservableWatchData<T> {
+  public abstract sealed class ObservableWatchData<T> {
     method @UiThread public final void addObserver(androidx.wear.watchface.Observer<T> observer);
     method @UiThread public T getValue();
     method @UiThread public final T getValueOr(T p);
@@ -138,6 +131,13 @@
     property @UiThread public T value;
   }
 
+  public static final class ObservableWatchData.MutableObservableWatchData<T> extends androidx.wear.watchface.ObservableWatchData<T> {
+    ctor public ObservableWatchData.MutableObservableWatchData(T? initialValue);
+    ctor public ObservableWatchData.MutableObservableWatchData();
+    method @UiThread public void setValue(T v);
+    property @UiThread public T value;
+  }
+
   public interface Observer<T> {
     method public void onChanged(T);
   }
diff --git a/wear/wear-watchface/api/restricted_current.txt b/wear/wear-watchface/api/restricted_current.txt
index 95a9235..23a2893 100644
--- a/wear/wear-watchface/api/restricted_current.txt
+++ b/wear/wear-watchface/api/restricted_current.txt
@@ -21,7 +21,7 @@
   }
 
   public interface CanvasComplicationFactory {
-    method @UiThread public androidx.wear.watchface.CanvasComplication create(androidx.wear.watchface.WatchState, androidx.wear.watchface.CanvasComplication.InvalidateCallback);
+    method @WorkerThread public androidx.wear.watchface.CanvasComplication create(androidx.wear.watchface.WatchState, androidx.wear.watchface.CanvasComplication.InvalidateCallback);
   }
 
   public final class Complication {
@@ -122,13 +122,6 @@
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class MutableObservableWatchData<T> extends androidx.wear.watchface.ObservableWatchData<T> {
-    ctor public MutableObservableWatchData(T? initialValue);
-    ctor public MutableObservableWatchData();
-    method @UiThread public void setValue(T v);
-    property @UiThread public T value;
-  }
-
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class MutableWatchState {
     method public androidx.wear.watchface.WatchState asWatchState();
     method public long getAnalogPreviewReferenceTimeMillis();
@@ -136,31 +129,31 @@
     method public long getDigitalPreviewReferenceTimeMillis();
     method public boolean getHasBurnInProtection();
     method public boolean getHasLowBitAmbient();
-    method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Integer> getInterruptionFilter();
-    method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isAmbient();
-    method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging();
+    method public androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Integer> getInterruptionFilter();
+    method public androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Boolean> isAmbient();
+    method public androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging();
     method public boolean isHeadless();
-    method public androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isVisible();
+    method public androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Boolean> isVisible();
     method public void setAnalogPreviewReferenceTimeMillis(long p);
     method public void setChinHeight(@Px int value);
     method public void setDigitalPreviewReferenceTimeMillis(long p);
     method public void setHasBurnInProtection(boolean p);
     method public void setHasLowBitAmbient(boolean p);
     method public void setHeadless(boolean p);
-    method public void setInterruptionFilter(androidx.wear.watchface.MutableObservableWatchData<java.lang.Integer> p);
+    method public void setInterruptionFilter(androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Integer> p);
     property public final long analogPreviewReferenceTimeMillis;
     property @Px public final int chinHeight;
     property public final long digitalPreviewReferenceTimeMillis;
     property public final boolean hasBurnInProtection;
     property public final boolean hasLowBitAmbient;
-    property public final androidx.wear.watchface.MutableObservableWatchData<java.lang.Integer> interruptionFilter;
-    property public final androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isAmbient;
-    property public final androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging;
+    property public final androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Integer> interruptionFilter;
+    property public final androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Boolean> isAmbient;
+    property public final androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging;
     property public final boolean isHeadless;
-    property public final androidx.wear.watchface.MutableObservableWatchData<java.lang.Boolean> isVisible;
+    property public final androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData<java.lang.Boolean> isVisible;
   }
 
-  public class ObservableWatchData<T> {
+  public abstract sealed class ObservableWatchData<T> {
     method @UiThread public final void addObserver(androidx.wear.watchface.Observer<T> observer);
     method @UiThread public T getValue();
     method @UiThread public final T getValueOr(T p);
@@ -170,6 +163,13 @@
     property @UiThread public T value;
   }
 
+  public static final class ObservableWatchData.MutableObservableWatchData<T> extends androidx.wear.watchface.ObservableWatchData<T> {
+    ctor public ObservableWatchData.MutableObservableWatchData(T? initialValue);
+    ctor public ObservableWatchData.MutableObservableWatchData();
+    method @UiThread public void setValue(T v);
+    property @UiThread public T value;
+  }
+
   public interface Observer<T> {
     method public void onChanged(T);
   }
@@ -368,7 +368,7 @@
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class WatchFaceImpl {
-    ctor @UiThread public WatchFaceImpl(androidx.wear.watchface.WatchFace watchface, androidx.wear.watchface.WatchFaceHostApi watchFaceHostApi, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationsManager complicationsManager);
+    ctor @UiThread public WatchFaceImpl(androidx.wear.watchface.WatchFace watchface, androidx.wear.watchface.WatchFaceHostApi watchFaceHostApi, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationsManager complicationsManager, android.icu.util.Calendar calendar);
     method public long getPreviewReferenceTimeMillis();
     property public final long previewReferenceTimeMillis;
   }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasComplicationFactory.java b/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasComplicationFactory.java
index 133b6d0..6d46712 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasComplicationFactory.java
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasComplicationFactory.java
@@ -17,7 +17,7 @@
 package androidx.wear.watchface;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
 
 /**
  * Factory for creating a CanvasComplication. This decouples construction of Complications and
@@ -26,7 +26,9 @@
 // TODO(b/188035300): Put links into the comments.
 public interface CanvasComplicationFactory {
     /**
-     * Creates a CanvasComplication.
+     * Creates a CanvasComplication. This will be called on a background thread, however all
+     * CanvasComplication rendering will be done on the UI thread and there's a memory barrier
+     * between construction and usage so no special threading primitives are required.
      *
      * @param watchState The current WatchState
      * @param invalidateCallback The CanvasComplication.InvalidateCallback the constructed
@@ -36,7 +38,7 @@
      * @return The constructed CanvasComplication.
      */
     @NonNull
-    @UiThread
+    @WorkerThread
     @SuppressWarnings("ExecutorRegistration") // This is UI thread only.
     CanvasComplication create(
             @NonNull WatchState watchState,
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
index 017b5675..062cdca 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
@@ -29,6 +29,7 @@
 import androidx.wear.complications.DefaultComplicationProviderPolicy
 import androidx.wear.complications.data.ComplicationData
 import androidx.wear.complications.data.ComplicationType
+import androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData
 import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.style.UserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.ComplicationsUserStyleSetting
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt
index 5a13a2d..a89c976 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt
@@ -33,6 +33,7 @@
 import androidx.wear.complications.data.ComplicationType
 import androidx.wear.complications.data.EmptyComplicationData
 import androidx.wear.utility.TraceEvent
+import androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData
 import androidx.wear.watchface.control.data.IdTypeAndDefaultProviderPolicyWireFormat
 import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.style.CurrentUserStyleRepository
@@ -152,6 +153,7 @@
     }
 
     /** Finish initialization. */
+    @WorkerThread
     internal fun init(
         watchFaceHostApi: WatchFaceHostApi,
         calendar: Calendar,
@@ -164,6 +166,9 @@
 
         for ((_, complication) in complications) {
             complication.init(complicationInvalidateListener)
+
+            // Force lazy construction of renderers.
+            complication.renderer
         }
 
         // Activate complications.
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt
index ba4ac3f..12b1443 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ObservableWatchData.kt
@@ -24,7 +24,7 @@
  * @param T The type of data held by this instance.
  * @param _value The initial value or `null` if there isn't an initial value.
  */
-public open class ObservableWatchData<T : Any> internal constructor(internal var _value: T?) {
+public sealed class ObservableWatchData<T : Any> constructor(internal var _value: T?) {
 
     private var iterating = false
     private val observers = ArrayList<Observer<T>>()
@@ -104,26 +104,26 @@
             "<unset>"
         }
     }
-}
-
-/**
- * [ObservableWatchData] which publicly exposes [setValue(T)] method.
- *
- * @param T The type of data held by this instance
- */
-public class MutableObservableWatchData<T : Any>(initialValue: T?) :
-    ObservableWatchData<T>(initialValue) {
-    public constructor() : this(null)
 
     /**
-     * Mutable observable value. Assigning a different value will trigger [Observer.onChanged]
-     * callbacks.
+     * [ObservableWatchData] which publicly exposes [setValue(T)] method.
+     *
+     * @param T The type of data held by this instance
      */
-    override var value: T
-        @UiThread
-        get() = _value!!
-        @UiThread
-        public set(v) {
-            super.value = v
-        }
+    public class MutableObservableWatchData<T : Any>(initialValue: T?) :
+        ObservableWatchData<T>(initialValue) {
+        public constructor() : this(null)
+
+        /**
+         * Mutable observable value. Assigning a different value will trigger [Observer.onChanged]
+         * callbacks.
+         */
+        override var value: T
+            @UiThread
+            get() = _value!!
+            @UiThread
+            public set(v) {
+                super.value = v
+            }
+    }
 }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index ff7ebb2..6dfc3f8 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -102,7 +102,8 @@
 
 /**
  * The base class for [CanvasRenderer] and [GlesRenderer]. Renderers are constructed on a background
- * thread but all rendering is done on the UiThread.
+ * thread but all rendering is done on the UiThread. There is a memory barrier between construction
+ * and rendering so no special threading primitives are required.
  *
  * @param surfaceHolder The [SurfaceHolder] that [renderInternal] will draw into.
  * @param currentUserStyleRepository The associated [CurrentUserStyleRepository].
@@ -306,7 +307,9 @@
      *
      * A CanvasRenderer is expected to be constructed on the background thread associated with
      * [WatchFaceService.getBackgroundThreadHandler] inside a call to
-     * [WatchFaceService.createWatchFace]. All rendering is be done on the UiThread.
+     * [WatchFaceService.createWatchFace]. All rendering is be done on the UiThread. There is a
+     * memory barrier between construction and rendering so no special threading primitives are
+     * required.
      *
      * @param surfaceHolder The [SurfaceHolder] from which a [Canvas] to will be obtained and passed
      * into [render].
@@ -463,7 +466,9 @@
      *
      * A GlesRenderer is expected to be constructed on the background thread associated with
      * [WatchFaceService.getBackgroundThreadHandler] inside a call to
-     * [WatchFaceService.createWatchFace]. All rendering is be done on the UiThread.
+     * [WatchFaceService.createWatchFace]. All rendering is be done on the UiThread. There is a
+     * memory barrier between construction and rendering so no special threading primitives are
+     * required.
      *
      * Two linked [EGLContext]s are created [eglBackgroundThreadContext] and [eglUiThreadContext]
      * which are associated with background and UiThread respectively. OpenGL objects created on
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index 3865712..ff3c3a2 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -19,7 +19,6 @@
 import android.annotation.SuppressLint
 import android.app.NotificationManager
 import android.content.ComponentName
-import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.graphics.Bitmap
@@ -32,7 +31,6 @@
 import android.os.Bundle
 import android.support.wearable.watchface.SharedMemoryImage
 import android.support.wearable.watchface.WatchFaceStyle
-import android.util.Base64
 import android.view.Gravity
 import android.view.Surface.FRAME_RATE_COMPATIBILITY_DEFAULT
 import androidx.annotation.ColorInt
@@ -41,7 +39,6 @@
 import androidx.annotation.Px
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
 import androidx.annotation.UiThread
 import androidx.annotation.VisibleForTesting
 import androidx.wear.complications.SystemProviders
@@ -49,6 +46,7 @@
 import androidx.wear.complications.data.ComplicationType
 import androidx.wear.complications.data.toApiComplicationData
 import androidx.wear.utility.TraceEvent
+import androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData
 import androidx.wear.watchface.control.data.ComplicationRenderParams
 import androidx.wear.watchface.control.data.WatchFaceRenderParams
 import androidx.wear.watchface.data.ComplicationStateWireFormat
@@ -58,10 +56,7 @@
 import androidx.wear.watchface.style.UserStyleData
 import androidx.wear.watchface.style.UserStyleSchema
 import androidx.wear.watchface.style.WatchFaceLayer
-import androidx.wear.watchface.style.data.UserStyleWireFormat
 import kotlinx.coroutines.CompletableDeferred
-import java.io.FileNotFoundException
-import java.io.InputStreamReader
 import java.security.InvalidParameterException
 import kotlin.math.max
 
@@ -94,35 +89,6 @@
     }
 }
 
-private fun readPrefs(context: Context, fileName: String): UserStyleWireFormat {
-    val hashMap = HashMap<String, ByteArray>()
-    try {
-        val reader = InputStreamReader(context.openFileInput(fileName)).buffered()
-        reader.use {
-            while (true) {
-                val key = reader.readLine() ?: break
-                val value = reader.readLine() ?: break
-                hashMap[key] = Base64.decode(value, Base64.NO_WRAP)
-            }
-        }
-    } catch (e: FileNotFoundException) {
-        // We don't need to do anything special here.
-    }
-    return UserStyleWireFormat(hashMap)
-}
-
-private fun writePrefs(context: Context, fileName: String, style: UserStyle) {
-    val writer = context.openFileOutput(fileName, Context.MODE_PRIVATE).bufferedWriter()
-    writer.use {
-        for ((key, value) in style.selectedOptions) {
-            writer.write(key.id.value)
-            writer.newLine()
-            writer.write(Base64.encodeToString(value.id.value, Base64.NO_WRAP))
-            writer.newLine()
-        }
-    }
-}
-
 /**
  * The return value of [WatchFaceService.createWatchFace] which brings together rendering, styling,
  * complications and state observers.
@@ -241,7 +207,7 @@
      * Interface for getting the current system time.
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public interface SystemTimeProvider {
         /** Returns the current system time in milliseconds. */
         public fun getSystemTimeMillis(): Long
@@ -370,7 +336,7 @@
     }
 
     /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun setSystemTimeProvider(systemTimeProvider: SystemTimeProvider): WatchFace = apply {
         this.systemTimeProvider = systemTimeProvider
     }
@@ -384,7 +350,12 @@
     private val watchFaceHostApi: WatchFaceHostApi,
     private val watchState: WatchState,
     internal val currentUserStyleRepository: CurrentUserStyleRepository,
-    internal var complicationsManager: ComplicationsManager
+    internal var complicationsManager: ComplicationsManager,
+
+    /** @hide */
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public val calendar: Calendar
 ) {
     internal companion object {
         internal const val NO_DEFAULT_PROVIDER = SystemProviders.NO_PROVIDER
@@ -459,11 +430,6 @@
     private var muteMode = false
     private var nextDrawTimeMillis: Long = 0
 
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public val calendar: Calendar = Calendar.getInstance()
-
     private val pendingUpdateTime: CancellableUniqueTask =
         CancellableUniqueTask(watchFaceHostApi.getUiThreadHandler())
 
@@ -602,35 +568,6 @@
     }
 
     init {
-        // If the system has a stored user style then Home/SysUI is in charge of style
-        // persistence, otherwise we need to do our own.
-        val storedUserStyle = watchFaceHostApi.getInitialUserStyle()
-        if (storedUserStyle != null) {
-            TraceEvent("WatchFaceImpl.init apply userStyle").use {
-                currentUserStyleRepository.userStyle =
-                    UserStyle(UserStyleData(storedUserStyle), currentUserStyleRepository.schema)
-            }
-        } else {
-            TraceEvent("WatchFaceImpl.init apply userStyle from prefs").use {
-                // The system doesn't support preference persistence we need to do it ourselves.
-                val preferencesFile =
-                    "watchface_prefs_${watchFaceHostApi.getContext().javaClass.name}.txt"
-
-                currentUserStyleRepository.userStyle = UserStyle(
-                    UserStyleData(readPrefs(watchFaceHostApi.getContext(), preferencesFile)),
-                    currentUserStyleRepository.schema
-                )
-
-                currentUserStyleRepository.addUserStyleChangeListener(
-                    object : CurrentUserStyleRepository.UserStyleChangeListener {
-                        @SuppressLint("SyntheticAccessor")
-                        override fun onUserStyleChanged(userStyle: UserStyle) {
-                            writePrefs(watchFaceHostApi.getContext(), preferencesFile, userStyle)
-                        }
-                    })
-            }
-        }
-
         renderer.watchFaceHostApi = watchFaceHostApi
         renderer.uiThreadInit()
 
@@ -640,34 +577,6 @@
             }
         )
 
-        // We need to inhibit an immediate callback during initialization because members are not
-        // fully constructed and it will fail. It's also superfluous because we're going to render
-        // anyway.
-        var initFinished = false
-        complicationsManager.init(
-            watchFaceHostApi, calendar, renderer,
-            object : Complication.InvalidateListener {
-                @SuppressWarnings("SyntheticAccessor")
-                override fun onInvalidate() {
-                    // This could be called on any thread.
-                    watchFaceHostApi.getUiThreadHandler().runOnHandlerWithTracing("onInvalidate") {
-                        // Ensure we render a frame if the Complication needs rendering, e.g.
-                        // because it loaded an image. However if we're animating there's no need
-                        // to trigger an extra invalidation.
-                        if (initFinished && (
-                            !renderer.shouldAnimate() || computeDelayTillNextFrame(
-                                    nextDrawTimeMillis,
-                                    systemTimeProvider.getSystemTimeMillis()
-                                ) > MIN_PERCEPTABLE_DELAY_MILLIS
-                            )
-                        ) {
-                            watchFaceHostApi.invalidate()
-                        }
-                    }
-                }
-            }
-        )
-
         if (!watchState.isHeadless) {
             WatchFace.registerEditorDelegate(componentName, WFEditorDelegate())
         }
@@ -678,8 +587,19 @@
         }
         watchState.interruptionFilter.addObserver(interruptionFilterObserver)
         watchState.isVisible.addObserver(visibilityObserver)
+    }
 
-        initFinished = true
+    internal fun invalidateIfNotAnimating() {
+        // Ensure we render a frame if the Complication needs rendering, e.g.
+        // because it loaded an image. However if we're animating there's no need
+        // to trigger an extra invalidation.
+        if (!renderer.shouldAnimate() || computeDelayTillNextFrame(
+                nextDrawTimeMillis,
+                systemTimeProvider.getSystemTimeMillis()
+            ) > MIN_PERCEPTABLE_DELAY_MILLIS
+        ) {
+            watchFaceHostApi.invalidate()
+        }
     }
 
     internal fun createWFEditorDelegate() = WFEditorDelegate() as WatchFace.EditorDelegate
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 5b988bc..567c93a 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -22,6 +22,7 @@
 import android.content.Intent
 import android.graphics.Canvas
 import android.graphics.Rect
+import android.icu.util.Calendar
 import android.os.Build
 import android.os.Bundle
 import android.os.Handler
@@ -35,6 +36,7 @@
 import android.support.wearable.watchface.IWatchFaceService
 import android.support.wearable.watchface.accessibility.AccessibilityUtils
 import android.support.wearable.watchface.accessibility.ContentDescriptionLabel
+import android.util.Base64
 import android.util.Log
 import android.view.Choreographer
 import android.view.Surface
@@ -79,8 +81,9 @@
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
 import java.io.FileDescriptor
+import java.io.FileNotFoundException
+import java.io.InputStreamReader
 import java.io.PrintWriter
 import java.util.concurrent.CountDownLatch
 
@@ -155,7 +158,8 @@
  * [UserStyleSetting]. To enable support for styling override [createUserStyleSchema].
  *
  * WatchFaces are initially constructed on a background thread before being used exclusively on
- * the ui thread afterwards.
+ * the ui thread afterwards. There is a memory barrier between construction and rendering so no
+ * special threading primitives are required.
  *
  * To aid debugging watch face animations, WatchFaceService allows you to speed up or slow down
  * time, and to loop between two instants.  This is controlled by MOCK_TIME_INTENT intents
@@ -281,7 +285,8 @@
      * library on the UiThread. If possible any expensive initialization should be done on a
      * background thread to avoid blocking the UiThread. This will be called from a background
      * thread but the [WatchFace] and its [Renderer] should be accessed exclusively from the
-     * UiThread afterwards.
+     * UiThread afterwards. There is a memory barrier between construction and rendering so no
+     * special threading primitives are required.
      *
      * Warning watch face initialization will fail if createWatchFace takes longer than
      * [MAX_CREATE_WATCHFACE_TIME_MILLIS] milliseconds.
@@ -316,8 +321,6 @@
     /** This is open for testing. */
     internal open fun getUiThreadHandlerImpl(): Handler = Handler(Looper.getMainLooper())
 
-    internal var backgroundThread: HandlerThread? = null
-
     /**
      * Returns the lazily constructed background thread [Handler]. During initialization
      * [createUserStyleSchema], [createComplicationsManager] and [createWatchFace] are posted on
@@ -325,16 +328,15 @@
      */
     public fun getBackgroundThreadHandler(): Handler = getBackgroundThreadHandlerImpl()
 
-    /** This is open for testing. */
-    internal open fun getBackgroundThreadHandlerImpl(): Handler {
-        if (backgroundThread == null) {
-            backgroundThread = HandlerThread("WatchFaceBackground").apply {
-                start()
-            }
-        }
-        return Handler(backgroundThread!!.looper)
+    internal val backgroundThread = lazy {
+        HandlerThread("WatchFaceBackground").apply { start() }
     }
 
+    private val _backgroundThreadHandler by lazy { Handler(backgroundThread.value.looper) }
+
+    /** This is open for testing. */
+    internal open fun getBackgroundThreadHandlerImpl() = _backgroundThreadHandler
+
     /** This is open to allow mocking. */
     internal open fun getMutableWatchState() = MutableWatchState()
 
@@ -358,6 +360,10 @@
         attachBaseContext(context)
     }
 
+    /**
+     * Reads WallpaperInteractiveWatchFaceInstanceParams from a file. This is only used in the
+     * android R flow.
+     */
     internal open fun readDirectBootPrefs(
         context: Context,
         fileName: String
@@ -375,6 +381,10 @@
         }
     }
 
+    /**
+     * Writes WallpaperInteractiveWatchFaceInstanceParams to a file. This is only used in the
+     * android R flow.
+     */
     internal open fun writeDirectBootPrefs(
         context: Context,
         fileName: String,
@@ -387,6 +397,37 @@
         }
     }
 
+    /** Reads user style from a file. This is only used in the pre-android R flow. */
+    internal fun readPrefs(context: Context, fileName: String): UserStyleWireFormat {
+        val hashMap = HashMap<String, ByteArray>()
+        try {
+            val reader = InputStreamReader(context.openFileInput(fileName)).buffered()
+            reader.use {
+                while (true) {
+                    val key = reader.readLine() ?: break
+                    val value = reader.readLine() ?: break
+                    hashMap[key] = Base64.decode(value, Base64.NO_WRAP)
+                }
+            }
+        } catch (e: FileNotFoundException) {
+            // We don't need to do anything special here.
+        }
+        return UserStyleWireFormat(hashMap)
+    }
+
+    /** Reads the user style to a file. This is only used in the pre-android R flow. */
+    internal fun writePrefs(context: Context, fileName: String, style: UserStyle) {
+        val writer = context.openFileOutput(fileName, Context.MODE_PRIVATE).bufferedWriter()
+        writer.use {
+            for ((key, value) in style.selectedOptions) {
+                writer.write(key.id.value)
+                writer.newLine()
+                writer.write(Base64.encodeToString(value.id.value, Base64.NO_WRAP))
+                writer.newLine()
+            }
+        }
+    }
+
     /** This is the old pre Android R flow that's needed for backwards compatibility. */
     internal class WslFlow(private val engineWrapper: EngineWrapper) {
         class PendingComplicationData(val complicationId: Int, val data: ComplicationData)
@@ -1032,7 +1073,9 @@
         }
 
         internal fun quitBackgroundThreadIfCreated() {
-            backgroundThread?.quitSafely()
+            if (backgroundThread.isInitialized()) {
+                backgroundThread.value.quitSafely()
+            }
         }
 
         @UiThread
@@ -1293,9 +1336,12 @@
                             "milliseconds."
                     }
 
+                    val calendar = Calendar.getInstance()
+                    val initStyleAndComplicationsDone = CompletableDeferred<Unit>()
+
                     // WatchFaceImpl (which registers broadcast observers) needs to be constructed
                     // on the UIThread.
-                    withContext(uiThreadCoroutineScope.coroutineContext) {
+                    uiThreadCoroutineScope.launch {
                         pendingInitialComplications?.let {
                             for (idAndData in it) {
                                 complicationsManager.onComplicationDataUpdate(
@@ -1305,20 +1351,35 @@
                             }
                         }
 
-                        require(getUiThreadHandler().looper.isCurrentThread)
                         TraceEvent("WatchFaceImpl.init").use {
-                            deferredWatchFaceImpl.complete(
-                                WatchFaceImpl(
-                                    watchFace,
-                                    this@EngineWrapper,
-                                    watchState,
-                                    currentUserStyleRepository,
-                                    complicationsManager
-                                )
+                            val watchFaceImpl = WatchFaceImpl(
+                                watchFace,
+                                this@EngineWrapper,
+                                watchState,
+                                currentUserStyleRepository,
+                                complicationsManager,
+                                calendar
                             )
+
+                            // Make sure no UI thread rendering (a consequence of completing
+                            // deferredWatchFaceImpl) occurs before initStyleAndComplications has
+                            // executed. NB usually we won't have to wait at all.
+                            initStyleAndComplicationsDone.await()
+                            deferredWatchFaceImpl.complete(watchFaceImpl)
                             asyncWatchFaceConstructionPending = false
                         }
                     }
+
+                    // Perform more initialization on the background thread.
+                    initStyleAndComplications(
+                        complicationsManager,
+                        currentUserStyleRepository,
+                        watchFace.renderer,
+                        calendar
+                    )
+
+                    // Now init has completed, it's OK to complete deferredWatchFaceImpl.
+                    initStyleAndComplicationsDone.complete(Unit)
                 } catch (e: Exception) {
                     Log.e(TAG, "WatchFace crashed during init", e)
                     deferredWatchFaceImpl.completeExceptionally(e)
@@ -1326,6 +1387,68 @@
             }
         }
 
+        /**
+         * It is OK to call this from a worker thread because we carefully ensure there's no
+         * concurrent writes to the ComplicationsManager. No UI thread rendering can be done until
+         * after this has completed.
+         */
+        @WorkerThread
+        internal fun initStyleAndComplications(
+            complicationsManager: ComplicationsManager,
+            currentUserStyleRepository: CurrentUserStyleRepository,
+            renderer: Renderer,
+            calendar: Calendar
+        ) = TraceEvent("initStyleAndComplications").use {
+            // If the system has a stored user style then Home/SysUI is in charge of style
+            // persistence, otherwise we need to do our own.
+            val storedUserStyle = getInitialUserStyle()
+            if (storedUserStyle != null) {
+                TraceEvent("WatchFaceImpl.init apply userStyle").use {
+                    currentUserStyleRepository.userStyle =
+                        UserStyle(UserStyleData(storedUserStyle), currentUserStyleRepository.schema)
+                }
+            } else {
+                TraceEvent("WatchFaceImpl.init apply userStyle from prefs").use {
+                    // The system doesn't support preference persistence we need to do it ourselves.
+                    val preferencesFile = "watchface_prefs_${_context.javaClass.name}.txt"
+
+                    currentUserStyleRepository.userStyle = UserStyle(
+                        UserStyleData(readPrefs(_context, preferencesFile)),
+                        currentUserStyleRepository.schema
+                    )
+
+                    currentUserStyleRepository.addUserStyleChangeListener(
+                        object : CurrentUserStyleRepository.UserStyleChangeListener {
+                            @SuppressLint("SyntheticAccessor")
+                            override fun onUserStyleChanged(userStyle: UserStyle) {
+                                writePrefs(_context, preferencesFile, userStyle)
+                            }
+                        }
+                    )
+                }
+            }
+
+            // We need to inhibit an immediate callback during initialization because members are
+            // not fully constructed and it will fail. It's also superfluous because we're going
+            // to render soon anyway.
+            var initFinished = false
+            complicationsManager.init(
+                this, calendar, renderer,
+                object : Complication.InvalidateListener {
+                    @SuppressWarnings("SyntheticAccessor")
+                    override fun onInvalidate() {
+                        // This could be called on any thread.
+                        uiThreadHandler.runOnHandlerWithTracing("onInvalidate") {
+                            if (initFinished) {
+                                getWatchFaceImplOrNull()?.invalidateIfNotAnimating()
+                            }
+                        }
+                    }
+                }
+            )
+            initFinished = true
+        }
+
         override fun onVisibilityChanged(visible: Boolean): Unit = TraceEvent(
             "onVisibilityChanged"
         ).use {
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index a32f6ce..d67a288 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -20,6 +20,7 @@
 import androidx.annotation.Px
 import androidx.annotation.RestrictTo
 import androidx.annotation.UiThread
+import androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData
 
 /**
  * Describes the current state of the wearable including some hardware details such as whether or
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ObservableWatchDataTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ObservableWatchDataTest.kt
index b29815d..8f796dd 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ObservableWatchDataTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ObservableWatchDataTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.wear.watchface
 
+import androidx.wear.watchface.ObservableWatchData.MutableObservableWatchData
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -30,7 +31,7 @@
 
 @Config(manifest = Config.NONE)
 @RunWith(WatchFaceTestRunner::class)
-class ObservableWatchDataTest {
+public class ObservableWatchDataTest {
     @Mock
     private lateinit var observer: Observer<Int>
 
@@ -41,39 +42,39 @@
     private lateinit var observer3: Observer<Int>
 
     @Before
-    fun setUp() {
+    public fun setUp() {
         MockitoAnnotations.initMocks(this)
     }
 
     @Test
-    fun initialValue() {
+    public fun initialValue() {
         val data = MutableObservableWatchData(10)
         assertThat(data.value).isEqualTo(10)
     }
 
     @Test
-    fun mutatedValue() {
+    public fun mutatedValue() {
         val data = MutableObservableWatchData(10)
         data.value = 20
         assertThat(data.value).isEqualTo(20)
     }
 
     @Test
-    fun addObserverNoData() {
+    public fun addObserverNoData() {
         val data = MutableObservableWatchData<Int>()
         data.addObserver(observer)
         verify(observer, never()).onChanged(any())
     }
 
     @Test
-    fun addObserver() {
+    public fun addObserver() {
         val data = MutableObservableWatchData(10)
         data.addObserver(observer)
         verify(observer).onChanged(10)
     }
 
     @Test
-    fun addObserverAndAssign() {
+    public fun addObserverAndAssign() {
         val data = MutableObservableWatchData(10)
         data.addObserver(observer)
         verify(observer).onChanged(10)
@@ -83,7 +84,7 @@
     }
 
     @Test
-    fun addObserverNoDataThenAssign() {
+    public fun addObserverNoDataThenAssign() {
         val data = MutableObservableWatchData<Int>()
         data.addObserver(observer)
 
@@ -92,7 +93,7 @@
     }
 
     @Test
-    fun addAndRemoveObserver() {
+    public fun addAndRemoveObserver() {
         val data = MutableObservableWatchData(10)
         data.addObserver(observer)
         data.removeObserver(observer)
@@ -103,7 +104,7 @@
     }
 
     @Test
-    fun removeObserverDuringCallback() {
+    public fun removeObserverDuringCallback() {
         val data = MutableObservableWatchData(10)
         data.addObserver(observer)
         data.addObserver(observer2)
@@ -124,7 +125,7 @@
     }
 
     @Test
-    fun addObserverInObserver() {
+    public fun addObserverInObserver() {
         val data = MutableObservableWatchData(10)
         var observersAdded = 0
         var addedObserverObservations = 0
diff --git a/wear/wear/src/main/java/androidx/wear/widget/CurvedTextView.java b/wear/wear/src/main/java/androidx/wear/widget/CurvedTextView.java
index b794874..3843c1e 100644
--- a/wear/wear/src/main/java/androidx/wear/widget/CurvedTextView.java
+++ b/wear/wear/src/main/java/androidx/wear/widget/CurvedTextView.java
@@ -40,6 +40,8 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.FloatRange;
@@ -909,6 +911,18 @@
         doUpdate();
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setText(mText);
+    }
+
+    @Override
+    public void onPopulateAccessibilityEvent(@NonNull AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+        event.getText().add(mText);
+    }
+
     /**
      * Nested class to avoid verification errors for methods induces in API level 26
      */
diff --git a/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt b/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt
index fc3f164..cd70bd7 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt
+++ b/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt
@@ -37,6 +37,8 @@
 
 @RunWith(AndroidJUnit4::class)
 @LargeTest
+@Suppress("DEPRECATION")
+// TODO: (b/189268580) Update this test to use the new constructors in MigrationTestHelper.
 class WorkDatabasePathHelperTest {
     @get:Rule
     val migrationTestHelper = MigrationTestHelper(