Merge "[ProfileInstaller] Introduce ProfileTranscoder to support N, O, P+ formats" into androidx-main
diff --git a/activity/integration-tests/testapp/build.gradle b/activity/integration-tests/testapp/build.gradle
index fdb5dec..11e5101 100644
--- a/activity/integration-tests/testapp/build.gradle
+++ b/activity/integration-tests/testapp/build.gradle
@@ -39,6 +39,6 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, 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 cc75f5d..95e55d2 100644
--- a/ads/ads-identifier-benchmark/build.gradle
+++ b/ads/ads-identifier-benchmark/build.gradle
@@ -35,5 +35,5 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
diff --git a/ads/ads-identifier-provider/build.gradle b/ads/ads-identifier-provider/build.gradle
index d412cd9..f1039b5 100644
--- a/ads/ads-identifier-provider/build.gradle
+++ b/ads/ads-identifier-provider/build.gradle
@@ -39,7 +39,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
 
 androidx {
diff --git a/ads/ads-identifier-testing/build.gradle b/ads/ads-identifier-testing/build.gradle
index 07d65f8..d8265c7 100644
--- a/ads/ads-identifier-testing/build.gradle
+++ b/ads/ads-identifier-testing/build.gradle
@@ -24,7 +24,7 @@
 dependencies {
     implementation(project(":ads-identifier-common"))
     api("androidx.annotation:annotation:1.1.0")
-    api(MOCKITO_CORE, libs.exclude_bytebuddy)
+    api(MOCKITO_CORE, excludes.bytebuddy)
 }
 
 android {
diff --git a/ads/ads-identifier/build.gradle b/ads/ads-identifier/build.gradle
index 6841cce..e3730f5 100644
--- a/ads/ads-identifier/build.gradle
+++ b/ads/ads-identifier/build.gradle
@@ -41,7 +41,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
 
 android {
diff --git a/appcompat/appcompat-resources/build.gradle b/appcompat/appcompat-resources/build.gradle
index 33879d3..6390db6 100644
--- a/appcompat/appcompat-resources/build.gradle
+++ b/appcompat/appcompat-resources/build.gradle
@@ -36,9 +36,9 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-runtime"), {
         exclude group: "androidx.appcompat", module: "appcompat-resources"
     })
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 99a5f06..29f8bf2 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -29,9 +29,9 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    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(project(":internal-testutils-appcompat"), {
         exclude group: "androidx.appcompat", module: "appcompat"
         exclude group: "androidx.core", module: "core"
diff --git a/arch/core/core-testing/build.gradle b/arch/core/core-testing/build.gradle
index f94fa7a..bd80e98 100644
--- a/arch/core/core-testing/build.gradle
+++ b/arch/core/core-testing/build.gradle
@@ -29,7 +29,7 @@
     api(project(":arch:core:core-runtime"))
     api("androidx.annotation:annotation:1.1.0")
     api(JUNIT)
-    api(MOCKITO_CORE, libs.exclude_bytebuddy)
+    api(MOCKITO_CORE, excludes.bytebuddy)
 
     testImplementation(JUNIT)
 
diff --git a/benchmark/macro-junit4/build.gradle b/benchmark/macro-junit4/build.gradle
index 4ee565e..d159e9f 100644
--- a/benchmark/macro-junit4/build.gradle
+++ b/benchmark/macro-junit4/build.gradle
@@ -50,9 +50,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
     // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     // DexMaker has it"s own MockMaker
 }
 
diff --git a/biometric/biometric-ktx/samples/src/main/java/androidx/biometric/samples/auth/CoroutineSamples.kt b/biometric/biometric-ktx/samples/src/main/java/androidx/biometric/samples/auth/CoroutineSamples.kt
index 9d9368e..d49de2e 100644
--- a/biometric/biometric-ktx/samples/src/main/java/androidx/biometric/samples/auth/CoroutineSamples.kt
+++ b/biometric/biometric-ktx/samples/src/main/java/androidx/biometric/samples/auth/CoroutineSamples.kt
@@ -102,7 +102,7 @@
 }
 
 @Sampled
-@Suppress("UnsafeNewApiCall", "NewApi")
+@Suppress("NewApi", "ClassVerificationFailure")
 suspend fun Fragment.class3BiometricAuth() {
     // To use Class3 authentication, we need to create a CryptoObject.
     // First create a spec for the key to be generated.
@@ -163,7 +163,7 @@
 }
 
 @Sampled
-@Suppress("UnsafeNewApiCall", "NewApi")
+@Suppress("NewApi", "ClassVerificationFailure")
 suspend fun Fragment.class3BiometricOrCredentialAuth() {
     // To use Class3 authentication, we need to create a CryptoObject.
     // First create a spec for the key to be generated.
@@ -224,7 +224,7 @@
 }
 
 @Sampled
-@Suppress("UnsafeNewApiCall", "NewApi")
+@Suppress("NewApi", "ClassVerificationFailure")
 suspend fun Fragment.credentialAuth() {
     // To use credential authentication, we need to create a CryptoObject.
     // First create a spec for the key to be generated.
diff --git a/biometric/biometric/build.gradle b/biometric/biometric/build.gradle
index 0933892..6f12943 100644
--- a/biometric/biometric/build.gradle
+++ b/biometric/biometric/build.gradle
@@ -46,8 +46,8 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
     androidTestImplementation(TRUTH)
 }
 
diff --git a/browser/browser/build.gradle b/browser/browser/build.gradle
index 5359fed..c98ffa8 100644
--- a/browser/browser/build.gradle
+++ b/browser/browser/build.gradle
@@ -40,9 +40,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-runtime"))
 }
 
diff --git a/buildSrc/dependencies.gradle b/buildSrc/dependencies.gradle
index 0eb002e..23c0358 100644
--- a/buildSrc/dependencies.gradle
+++ b/buildSrc/dependencies.gradle
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 // Add ext.libs for library versions
-def libs = [:]
+def excludes = [:]
 
-libs.exclude_bytebuddy = {
+excludes.bytebuddy = {
     exclude group: "net.bytebuddy"
 }
 
-libs.exclude_for_espresso = {
+excludes.espresso = {
     exclude group: "androidx.annotation"
     exclude group: "androidx.appcompat"
     exclude group: "androidx.recyclerview"
@@ -31,4 +31,4 @@
     exclude group: "androidx.core"
 }
 
-rootProject.ext["libs"] = libs
+rootProject.ext["excludes"] = excludes
diff --git a/buildSrc/src/main/kotlin/androidx/build/BundleInsideHelper.kt b/buildSrc/src/main/kotlin/androidx/build/BundleInsideHelper.kt
index 7d9d5d0..be96014 100644
--- a/buildSrc/src/main/kotlin/androidx/build/BundleInsideHelper.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/BundleInsideHelper.kt
@@ -40,12 +40,13 @@
      *
      * Used project are expected
      *
+     * @see forInsideAar(String, String)
+     *
      * @receiver the project that should bundle jars specified by these configurations
-     * @param from specifies from which package the rename should happen
-     * @param to specifies to which package to put the renamed classes
+     * @param relocations a list of package relocations to apply
      */
     @JvmStatic
-    fun Project.forInsideAar(from: String, to: String) {
+    fun Project.forInsideAar(relocations: List<Relocation>) {
         val bundle = configurations.create("bundleInside")
         val bundleDebug = configurations.create("debugBundleInside") {
             it.extendsFrom(bundle)
@@ -53,8 +54,12 @@
         val bundleRelease = configurations.create("releaseBundleInside") {
             it.extendsFrom(bundle)
         }
-        val repackageRelease = configureRepackageTaskForType("Release", from, to, bundleRelease)
-        val repackageDebug = configureRepackageTaskForType("Debug", from, to, bundleDebug)
+        val repackageRelease = configureRepackageTaskForType(
+            "Release",
+            relocations,
+            bundleRelease
+        )
+        val repackageDebug = configureRepackageTaskForType("Debug", relocations, bundleDebug)
 
         // Add to AGP's configurations so these jars get packaged inside of the aar.
         dependencies.add(
@@ -69,6 +74,26 @@
             task.dependsOn(repackageRelease)
         }
     }
+    /**
+     * Creates 3 configurations for the users to use that will be used bundle these dependency
+     * jars inside of libs/ directory inside of the aar.
+     *
+     * ```
+     * dependencies {
+     *   bundleInside(project(":foo"))
+     * }
+     * ```
+     *
+     * Used project are expected
+     *
+     * @receiver the project that should bundle jars specified by these configurations
+     * @param from specifies from which package the rename should happen
+     * @param to specifies to which package to put the renamed classes
+     */
+    @JvmStatic
+    fun Project.forInsideAar(from: String, to: String) {
+        forInsideAar(listOf(Relocation(from, to)))
+    }
 
     /**
      * Creates a configuration for the users to use that will be used bundle these dependency
@@ -88,7 +113,11 @@
     @JvmStatic
     fun Project.forInsideJar(from: String, to: String) {
         val bundle = configurations.create("bundleInside")
-        val repackage = configureRepackageTaskForType("jar", from, to, bundle)
+        val repackage = configureRepackageTaskForType(
+            "jar",
+            listOf(Relocation(from, to)),
+            bundle
+        )
         dependencies.add("compileOnly", files(repackage.flatMap { it.archiveFile }))
         dependencies.add("testImplementation", files(repackage.flatMap { it.archiveFile }))
 
@@ -165,10 +194,11 @@
         }
     }
 
+    data class Relocation(val from: String, val to: String)
+
     private fun Project.configureRepackageTaskForType(
         type: String,
-        from: String,
-        to: String,
+        relocations: List<Relocation>,
         configuration: Configuration
     ): TaskProvider<ShadowJar> {
         return tasks.register(
@@ -177,7 +207,9 @@
         ) { task ->
             task.apply {
                 configurations = listOf(configuration)
-                relocate(from, to)
+                for (relocation in relocations) {
+                    relocate(relocation.from, relocation.to)
+                }
                 archiveBaseName.set("repackaged-$type")
                 archiveVersion.set("")
                 destinationDirectory.set(File(buildDir, "repackaged"))
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 113c7b3..78ccd42 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -55,8 +55,8 @@
     androidTestImplementation(TRUTH)
     androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0")
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(KOTLIN_STDLIB)
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index 32b510d..f3e2dd0 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -28,6 +28,7 @@
   }
 
   public interface CameraInfo {
+    method @androidx.camera.core.ExperimentalCameraFilter public androidx.camera.core.CameraSelector getCameraSelector();
     method @androidx.camera.core.ExperimentalExposureCompensation public androidx.camera.core.ExposureState getExposureState();
     method public int getSensorRotationDegrees();
     method public int getSensorRotationDegrees(int);
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index 1ee46cb..2b4bf00 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -57,8 +57,8 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
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 43378ac..1eb2ff3 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
@@ -178,6 +178,15 @@
     @ImplementationType
     String getImplementationType();
 
+    /**
+     * Returns a {@link CameraSelector} unique to this camera.
+     *
+     * @return {@link CameraSelector} unique to this camera.
+     */
+    @ExperimentalCameraFilter
+    @NonNull
+    CameraSelector getCameraSelector();
+
     /** @hide */
     @StringDef(open = true, value = {IMPLEMENTATION_TYPE_UNKNOWN,
             IMPLEMENTATION_TYPE_CAMERA2_LEGACY, IMPLEMENTATION_TYPE_CAMERA2,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.java
index 5e11df0..a8e42bd 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraProvider.java
@@ -50,6 +50,11 @@
      * selected through
      * {@link androidx.camera.core.CameraXConfig.Builder#setAvailableCamerasLimiter(CameraSelector)}
      *
+     * <p>While iterating through all the available {@link CameraInfo}, if one of them meets some
+     * predefined requirements, a {@link CameraSelector} that uniquely identifies its camera
+     * can be retrieved using {@link CameraInfo#getCameraSelector()}, which can then be used to bind
+     * {@linkplain UseCase use cases} to that camera.
+     *
      * @return A list of {@link CameraInfo} instances for the available cameras.
      */
     @NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
index aba76ae..43c07d5 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
@@ -19,7 +19,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.camera.core.CameraInfo;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.ExperimentalCameraFilter;
+import androidx.core.util.Preconditions;
 
+import java.util.Collections;
 import java.util.concurrent.Executor;
 
 /**
@@ -70,4 +74,26 @@
     /** Returns the {@link CamcorderProfileProvider} associated with this camera. */
     @NonNull
     CamcorderProfileProvider getCamcorderProfileProvider();
+
+    /** {@inheritDoc} */
+    @ExperimentalCameraFilter
+    @NonNull
+    @Override
+    default CameraSelector getCameraSelector() {
+        return new CameraSelector.Builder()
+                .addCameraFilter(cameraInfos -> {
+                    final String cameraId = getCameraId();
+                    for (CameraInfo cameraInfo : cameraInfos) {
+                        Preconditions.checkArgument(cameraInfo instanceof CameraInfoInternal);
+                        final CameraInfoInternal cameraInfoInternal =
+                                (CameraInfoInternal) cameraInfo;
+                        if (cameraInfoInternal.getCameraId().equals(cameraId)) {
+                            return Collections.singletonList(cameraInfo);
+                        }
+                    }
+                    throw new IllegalStateException("Unable to find camera with id " + cameraId
+                            + " from list of available cameras.");
+                })
+                .build();
+    }
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/CameraInfoInternalTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/impl/CameraInfoInternalTest.kt
new file mode 100644
index 0000000..7c083c9
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/CameraInfoInternalTest.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.core.impl
+
+import android.os.Build
+import androidx.camera.testing.fakes.FakeCamera
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+private const val CAMERA_ID = "2"
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+internal class CameraInfoInternalTest {
+
+    @Test
+    fun selector_findsMatchingCamera() {
+        val cameraInfo = FakeCameraInfoInternal(CAMERA_ID)
+        val cameras = createCamerasWithIds(arrayOf(1, CAMERA_ID.toInt(), 3, 4))
+        val filteredCameras = cameraInfo.cameraSelector.filter(LinkedHashSet(cameras))
+
+        assertThat(filteredCameras).hasSize(1)
+        assertThat(filteredCameras.first().cameraInfoInternal.cameraId).isEqualTo(CAMERA_ID)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun selector_doesNotFindMatchingCamera() {
+        val cameraInfo = FakeCameraInfoInternal(CAMERA_ID)
+        val cameras = createCamerasWithIds(arrayOf(1, 3, 4))
+        cameraInfo.cameraSelector.filter(LinkedHashSet(cameras))
+    }
+
+    private fun createCamerasWithIds(ids: Array<Int>): List<CameraInternal> {
+        return ids.map { FakeCamera(it.toString()) }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 1d55af9..64d147b 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -49,9 +49,9 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
     androidTestImplementation(TRUTH)
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(project(":internal-testutils-truth"))
diff --git a/camera/camera-video/build.gradle b/camera/camera-video/build.gradle
index d2d4178..4527837 100644
--- a/camera/camera-video/build.gradle
+++ b/camera/camera-video/build.gradle
@@ -53,8 +53,8 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(KOTLIN_COROUTINES_ANDROID)
diff --git a/camera/camera-view/build.gradle b/camera/camera-view/build.gradle
index aef81d0..06c00c8 100644
--- a/camera/camera-view/build.gradle
+++ b/camera/camera-view/build.gradle
@@ -73,8 +73,8 @@
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(TRUTH)
     androidTestImplementation("androidx.camera:camera-camera2:${VIEW_ATOMIC_GROUP_PINNED_VER}")
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
 }
 android {
     defaultConfig {
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
index 8ffe920..c2ba48a 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
@@ -49,6 +49,10 @@
         android:resource="@style/CarAppTheme"
         tools:ignore="MetadataTagInsideApplicationTag" />
 
+    <meta-data android:name="androidx.car.app.minApiLevel"
+        android:value="1"
+        tools:ignore="MetadataTagInsideApplicationTag" />
+
     <service
         android:name="androidx.car.app.sample.showcase.common.ShowcaseService"
         android:exported="true">
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/LongMessageTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/LongMessageTemplateDemoScreen.java
index 28b8d61..a94713e 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/LongMessageTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/LongMessageTemplateDemoScreen.java
@@ -28,7 +28,9 @@
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarColor;
 import androidx.car.app.model.LongMessageTemplate;
+import androidx.car.app.model.MessageTemplate;
 import androidx.car.app.model.Template;
+import androidx.car.app.versioning.CarAppApiLevels;
 
 /** A screen that demonstrates the long message template. */
 @OptIn(markerClass = androidx.car.app.annotations.ExperimentalCarApi.class)
@@ -83,6 +85,12 @@
     @NonNull
     @Override
     public Template onGetTemplate() {
+        if (getCarContext().getCarAppApiLevel() < CarAppApiLevels.LEVEL_2) {
+            return new MessageTemplate.Builder("Your host doesn't support Long Message template")
+                    .setTitle("Incompatible host")
+                    .setHeaderAction(Action.BACK)
+                    .build();
+        }
         return new LongMessageTemplate.Builder(TEXT)
                 .setTitle("Long Message Template Demo")
                 .setHeaderAction(BACK)
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 000eb03..113d018 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
@@ -41,6 +41,7 @@
 import androidx.car.app.model.signin.SignInTemplate;
 import androidx.car.app.sample.showcase.common.R;
 import androidx.car.app.sample.showcase.common.common.Utils;
+import androidx.car.app.versioning.CarAppApiLevels;
 import androidx.core.graphics.drawable.IconCompat;
 
 import java.util.UUID;
@@ -108,6 +109,12 @@
     @NonNull
     @Override
     public Template onGetTemplate() {
+        if (getCarContext().getCarAppApiLevel() < CarAppApiLevels.LEVEL_2) {
+            return new MessageTemplate.Builder("Your host doesn't support Sign In template")
+                    .setTitle("Incompatible host")
+                    .setHeaderAction(Action.BACK)
+                    .build();
+        }
         switch (mState) {
             case USERNAME:
                 return getUsernameSignInTemplate();
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 4197448..d79c285 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -6,6 +6,7 @@
     method public int getLatestCarAppApiLevel();
     method public String getLibraryDisplayVersion();
     method public int getMinCarAppApiLevel();
+    field public static final String MIN_API_LEVEL_MANIFEST_KEY = "androidx.car.app.minApiLevel";
   }
 
   public class AppManager {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 0c92952a..892476a 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -6,6 +6,7 @@
     method public int getLatestCarAppApiLevel();
     method public String getLibraryDisplayVersion();
     method public int getMinCarAppApiLevel();
+    field public static final String MIN_API_LEVEL_MANIFEST_KEY = "androidx.car.app.minApiLevel";
   }
 
   public class AppManager {
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 4197448..d79c285 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -6,6 +6,7 @@
     method public int getLatestCarAppApiLevel();
     method public String getLibraryDisplayVersion();
     method public int getMinCarAppApiLevel();
+    field public static final String MIN_API_LEVEL_MANIFEST_KEY = "androidx.car.app.minApiLevel";
   }
 
   public class AppManager {
diff --git a/car/app/app/src/main/java/androidx/car/app/AppInfo.java b/car/app/app/src/main/java/androidx/car/app/AppInfo.java
index 7fdc8bb..a2a1b6a 100644
--- a/car/app/app/src/main/java/androidx/car/app/AppInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/AppInfo.java
@@ -48,7 +48,7 @@
  * <manifest ...>
  *   <application ...>
  *     <meta-data
- *         android:name="androidx.car.app.min-api-level"
+ *         android:name="androidx.car.app.minApiLevel"
  *         android:value="1" />
  *     ...
  *   </application>
@@ -62,10 +62,16 @@
     // TODO(b/174803562): Automatically update the this version using Gradle
     private static final String LIBRARY_VERSION = "1.1.0-alpha01";
 
-    /** @hide */
-    @RestrictTo(Scope.LIBRARY)
-    @VisibleForTesting
-    public static final String MIN_API_LEVEL_MANIFEST_KEY = "androidx.car.app.min-api-level";
+    /**
+     * Application meta-data tag used to define the minimum Car App API level this application is
+     * able to handle.
+     *
+     * <p>If not specified, the library assumes the application can only handle the Car App API
+     * level designed by {@link CarAppApiLevels#getLatest()} at the time of compiling.
+     *
+     * @see CarContext#getCarAppApiLevel()
+     */
+    public static final String MIN_API_LEVEL_MANIFEST_KEY = "androidx.car.app.minApiLevel";
 
     @Keep
     @Nullable
diff --git a/cleanBuild.sh b/cleanBuild.sh
index 3ee4ae6..c6ed4dd 100755
--- a/cleanBuild.sh
+++ b/cleanBuild.sh
@@ -1,19 +1,6 @@
 #!/bin/bash
 set -e
 
-echo "IF THIS SCRIPT FIXES YOUR BUILD; OPEN A BUG."
-echo "In nearly all cases, it should not be necessary to run a clean build."
-echo
-echo "You may be more interested in running:"
-echo
-echo "  ./development/diagnose-build-failure/diagnose-build-failure.sh $*"
-echo
-echo "which attempts to diagnose more details about build failures"
-# one case where it is convenient to have a clean build is for double-checking that a build failure isn't due to an incremental build failure
-# another case where it is convenient to have a clean build is for performance testing
-# another case where it is convenient to have a clean build is when you're modifying the build and may have introduced some errors but haven't shared your changes yet (at which point you should have fixed the errors)
-echo
-
 DO_PROMPT=true
 if [ "$1" == "-y" ]; then
   DO_PROMPT=false
@@ -52,7 +39,7 @@
   # Confirm whether the user wants to run this script instead of diagnose-build-failure.sh
   # Recall that we already mentioned the existence of diagnose-build-failure.sh above
   echo
-  echo "Press <Enter> to run a clean build or Ctrl-C to cancel"
+  echo "Press <Enter> to run a clean build (./gradlew --clean $goals) or Ctrl-C to cancel"
   if [ "$DO_PROMPT" == "true" ]; then
     read response
   fi
@@ -62,25 +49,5 @@
 scriptDir="$(cd $(dirname $0) && pwd)"
 checkoutDir="$(cd $scriptDir/../.. && pwd)"
 export OUT_DIR="$checkoutDir/out"
-function removeCaches() {
-  echo removing caches
-  rm -rf .gradle
-  rm -rf buildSrc/.gradle
-  rm -f local.properties
-  # We move the Gradle cache (via the OUT_DIR) variable during this build to prevent
-  # Other Gradle builds in other directories from sharing it, to be extra-sure that the
-  # build will be clean. However, if the user subsequently runs `./gradlew`, it will use
-  # ~/.gradle as the Gradle cache dir, which could surprise users because it might hold
-  # different state. So, we preemptively remove ~/.gradle too, just in case the user
-  # is going to want that for their following build
-  rm -rf ~/.gradle
-  # AGP should (also) do this automatically (b/170640263)
-  rm -rf appsearch/appsearch/.cxx
-  rm -rf appsearch/local-backend/.cxx
-  rm -rf appsearch/local-storage/.cxx
-  rm -rf $OUT_DIR
-}
-removeCaches
 
-echo running build
-./gradlew --no-daemon $goals
+./gradlew --clean $goals
diff --git a/compose/animation/animation-lint/src/main/java/androidx/compose/animation/lint/CrossfadeDetector.kt b/compose/animation/animation-lint/src/main/java/androidx/compose/animation/lint/CrossfadeDetector.kt
index f3edff4..9dcc564 100644
--- a/compose/animation/animation-lint/src/main/java/androidx/compose/animation/lint/CrossfadeDetector.kt
+++ b/compose/animation/animation-lint/src/main/java/androidx/compose/animation/lint/CrossfadeDetector.kt
@@ -48,7 +48,8 @@
 
     override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
         if (method.isInPackageName(Names.Animation.PackageName)) {
-            val lambdaArgument = node.valueArguments.filterIsInstance<ULambdaExpression>().first()
+            val lambdaArgument = node.valueArguments.filterIsInstance<ULambdaExpression>()
+                .firstOrNull() ?: return
 
             lambdaArgument.findUnreferencedParameters().forEach { unreferencedParameter ->
                 val location = unreferencedParameter.parameter
diff --git a/compose/animation/animation-lint/src/test/java/androidx/compose/animation/lint/CrossfadeDetectorTest.kt b/compose/animation/animation-lint/src/test/java/androidx/compose/animation/lint/CrossfadeDetectorTest.kt
index 9b483b1..180b874 100644
--- a/compose/animation/animation-lint/src/test/java/androidx/compose/animation/lint/CrossfadeDetectorTest.kt
+++ b/compose/animation/animation-lint/src/test/java/androidx/compose/animation/lint/CrossfadeDetectorTest.kt
@@ -177,6 +177,9 @@
                 Crossfade(foo) { param -> if (param) { /**/ } else { /**/ } }
                 Crossfade(foo, content = { param -> if (param) { /**/ } else { /**/ } })
 
+                val content : @Composable (Boolean) -> Unit = {}
+                Crossfade(foo, content = content)
+
                 Crossfade(foo) { param ->
                     foo.let {
                         it.let {
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 80a5550..7d4b5d7 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -70,9 +70,9 @@
         androidTestImplementation(ESPRESSO_CORE)
         androidTestImplementation(JUNIT)
         androidTestImplementation(TRUTH)
-        androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own
+        androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own
         // MockMaker
-        androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+        androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
         androidTestImplementation(MOCKITO_KOTLIN, {
             exclude group: "org.mockito" // to keep control on the mockito version
         })
@@ -136,9 +136,9 @@
                 implementation(ESPRESSO_CORE)
                 implementation(JUNIT)
                 implementation(TRUTH)
-                implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own
+                implementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own
                 // MockMaker
-                implementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+                implementation(MOCKITO_CORE, excludes.bytebuddy)
                 // DexMaker has it"s own MockMaker
                 implementation(MOCKITO_KOTLIN, {
                     exclude group: "org.mockito" // to keep control on the mockito version
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index dbc14ae..b7f40da 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -61,8 +61,8 @@
         androidTestImplementation(ANDROIDX_TEST_RUNNER)
         androidTestImplementation(JUNIT)
         androidTestImplementation(TRUTH)
-        androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-        androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+        androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+        androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
         androidTestImplementation(MOCKITO_KOTLIN, {
             exclude group: "org.mockito" // to keep control on the mockito version
         })
@@ -120,8 +120,8 @@
                 implementation(ANDROIDX_TEST_RUNNER)
                 implementation(JUNIT)
                 implementation(TRUTH)
-                implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-                implementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+                implementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+                implementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
                 implementation(MOCKITO_KOTLIN, {
                     exclude group: "org.mockito" // to keep control on the mockito version
                 })
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
index 3432747..c77f622 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
@@ -18,20 +18,16 @@
 
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.tween
-import androidx.compose.foundation.interaction.FocusInteraction
-import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
-import kotlinx.coroutines.flow.collect
 
 /**
  * Represents the colors of the input text, background and content (including label, placeholder,
@@ -327,25 +323,12 @@
         isError: Boolean,
         interactionSource: InteractionSource
     ): State<Color> {
-        val interactions = remember { mutableStateListOf<Interaction>() }
-        LaunchedEffect(interactionSource) {
-            interactionSource.interactions.collect { interaction ->
-                when (interaction) {
-                    is FocusInteraction.Focus -> {
-                        interactions.add(interaction)
-                    }
-                    is FocusInteraction.Unfocus -> {
-                        interactions.remove(interaction.focus)
-                    }
-                }
-            }
-        }
-        val interaction = interactions.lastOrNull()
+        val focused by interactionSource.collectIsFocusedAsState()
 
         val targetValue = when {
             !enabled -> disabledIndicatorColor
             isError -> errorIndicatorColor
-            interaction is FocusInteraction.Focus -> focusedIndicatorColor
+            focused -> focusedIndicatorColor
             else -> unfocusedIndicatorColor
         }
         return if (enabled) {
@@ -371,25 +354,12 @@
         error: Boolean,
         interactionSource: InteractionSource
     ): State<Color> {
-        val interactions = remember { mutableStateListOf<Interaction>() }
-        LaunchedEffect(interactionSource) {
-            interactionSource.interactions.collect { interaction ->
-                when (interaction) {
-                    is FocusInteraction.Focus -> {
-                        interactions.add(interaction)
-                    }
-                    is FocusInteraction.Unfocus -> {
-                        interactions.remove(interaction.focus)
-                    }
-                }
-            }
-        }
-        val interaction = interactions.lastOrNull()
+        val focused by interactionSource.collectIsFocusedAsState()
 
         val targetValue = when {
             !enabled -> disabledLabelColor
             error -> errorLabelColor
-            interaction is FocusInteraction.Focus -> focusedLabelColor
+            focused -> focusedLabelColor
             else -> unfocusedLabelColor
         }
         return if (enabled) {
diff --git a/compose/runtime/runtime-saveable/build.gradle b/compose/runtime/runtime-saveable/build.gradle
index eaf9e66..2c030ea 100644
--- a/compose/runtime/runtime-saveable/build.gradle
+++ b/compose/runtime/runtime-saveable/build.gradle
@@ -57,9 +57,9 @@
         androidTestImplementation(ESPRESSO_CORE)
         androidTestImplementation(JUNIT)
         androidTestImplementation(TRUTH)
-        androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+        androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
         // DexMaker has it"s own MockMaker
-        androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+        androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
         // DexMaker has it"s own MockMaker
 
         lintPublish(project(":compose:runtime:runtime-saveable-lint"))
@@ -105,9 +105,9 @@
                 implementation(ESPRESSO_CORE)
                 implementation(JUNIT)
                 implementation(TRUTH)
-                implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+                implementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
                 // DexMaker has it"s own MockMaker
-                implementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+                implementation(MOCKITO_CORE, excludes.bytebuddy)
                 // DexMaker has it"s own MockMaker
             }
         }
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 87b2e16..b40bfcc 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
@@ -153,47 +153,46 @@
         val builder = LayoutInspectorTree()
         val nodes = builder.convert(view)
         dumpNodes(nodes, view, builder)
+        val top = findTopPosition(view)
 
         validate(nodes, builder) {
             node(
                 name = "Column",
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 0.0.dp, top = 0.0.dp, width = 72.0.dp, height = 78.9.dp,
+                left = 0.0.dp, top = top, width = 72.0.dp, height = 78.9.dp,
                 children = listOf("Text", "Icon", "Surface")
             )
             node(
                 name = "Text",
                 isRenderNode = true,
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 0.0.dp, top = 0.0.dp, width = 72.0.dp, height = 18.9.dp,
+                left = 0.0.dp, top = top, width = 72.0.dp, height = 18.9.dp,
             )
             node(
                 name = "Icon",
                 isRenderNode = true,
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 0.0.dp, top = 18.9.dp, width = 24.0.dp, height = 24.0.dp,
+                left = 0.0.dp, top = top + 18.9.dp, width = 24.0.dp, height = 24.0.dp,
             )
             node(
                 name = "Surface",
                 fileName = "LayoutInspectorTreeTest.kt",
                 isRenderNode = true,
-                left = 0.0.dp,
-                top = 42.9.dp, width = 64.0.dp, height = 36.0.dp,
+                left = 0.0.dp, top = top + 42.9.dp, width = 64.0.dp, height = 36.0.dp,
                 children = listOf("Button")
             )
             node(
                 name = "Button",
                 fileName = "LayoutInspectorTreeTest.kt",
                 isRenderNode = true,
-                left = 0.0.dp,
-                top = 42.9.dp, width = 64.0.dp, height = 36.0.dp,
+                left = 0.0.dp, top = top + 42.9.dp, width = 64.0.dp, height = 36.0.dp,
                 children = listOf("Text")
             )
             node(
                 name = "Text",
                 isRenderNode = true,
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 21.7.dp, top = 51.6.dp, width = 20.9.dp, height = 18.9.dp,
+                left = 21.7.dp, top = top + 51.5.dp, width = 20.9.dp, height = 18.9.dp,
             )
         }
     }
@@ -219,13 +218,14 @@
         val builder = LayoutInspectorTree()
         val nodes = builder.convert(view)
         dumpNodes(nodes, view, builder)
+        val top = findTopPosition(view)
 
         validate(nodes, builder) {
             node(
                 name = "MaterialTheme",
                 hasTransformations = true,
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 68.0.dp, top = 49.7.dp, width = 88.6.dp, height = 21.7.dp,
+                left = 68.0.dp, top = top + 49.8.dp, width = 88.6.dp, height = 21.7.dp,
                 children = listOf("Text")
             )
             node(
@@ -233,7 +233,7 @@
                 isRenderNode = true,
                 hasTransformations = true,
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 68.0.dp, top = 49.7.dp, width = 88.6.dp, height = 21.7.dp,
+                left = 68.0.dp, top = top + 49.8.dp, width = 88.6.dp, height = 21.7.dp,
             )
         }
     }
@@ -477,24 +477,59 @@
         val builder = LayoutInspectorTree()
         val nodes = builder.convert(appView)
         dumpNodes(nodes, appView, builder)
+        val top = findTopPosition(appView)
 
         // Verify that there are no Composable nodes from the dialog in the application itself:
         validate(nodes, builder) {
             node(
                 name = "Column",
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 0.0.dp, top = 0.0.dp, width = 76.0.dp, height = 18.9.dp,
+                left = 0.0.dp, top = top, width = 76.0.dp, height = 18.9.dp,
                 children = listOf("Text")
             )
             node(
                 name = "Text",
                 isRenderNode = true,
                 fileName = "LayoutInspectorTreeTest.kt",
-                left = 0.0.dp, top = 0.0.dp, width = 76.0.dp, height = 18.9.dp,
+                left = 0.0.dp, top = top, width = 76.0.dp, height = 18.9.dp,
             )
         }
     }
 
+    @Test
+    fun testDialogLocation() {
+        val slotTableRecord = CompositionDataRecord.create()
+
+        show {
+            Inspectable(slotTableRecord) {
+                Column {
+                    Text("Hello World!")
+                    AlertDialog(
+                        onDismissRequest = {},
+                        confirmButton = {
+                            Button({}) {
+                                Text("This is the Confirm Button")
+                            }
+                        }
+                    )
+                }
+            }
+        }
+        val composeViews = findAllAndroidComposeViews()
+        val dialogView = composeViews[1] // composeView[0] contains the contents of the app
+        val dialogLocation = IntArray(2)
+        dialogView.getLocationOnScreen(dialogLocation)
+        dialogView.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
+        val builder = LayoutInspectorTree()
+        val button = builder.convert(dialogView)
+            .flatMap { flatten(it) }
+            .single { it.name == "Button" }
+        assertThat(button.left).isGreaterThan(dialogLocation[0])
+        assertThat(button.top).isGreaterThan(dialogLocation[1])
+        assertThat(button.width).isLessThan(dialogView.width)
+        assertThat(button.height).isLessThan(dialogView.height)
+    }
+
     @Suppress("SameParameterValue")
     private fun validate(
         result: List<InspectorNode>,
@@ -592,6 +627,12 @@
     private fun flatten(node: InspectorNode): List<InspectorNode> =
         listOf(node).plus(node.children.flatMap { flatten(it) })
 
+    private fun findTopPosition(view: View): Dp {
+        val location = IntArray(2)
+        view.getLocationOnScreen(location)
+        return with(density) { location[1].toDp() }
+    }
+
     // region DEBUG print methods
     private fun dumpNodes(nodes: List<InspectorNode>, view: View, builder: LayoutInspectorTree) {
         @Suppress("ConstantConditionIf")
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 ccacc3b..68a2185 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
@@ -85,6 +85,7 @@
     private val parameterFactory = ParameterFactory(inlineClassConverter)
     private val cache = ArrayDeque<MutableInspectorNode>()
     private var generatedId = -1L
+    private val rootLocation = IntArray(2)
     /** Map from [LayoutInfo] to the nearest [InspectorNode] that contains it */
     private val claimedNodes = IdentityHashMap<LayoutInfo, InspectorNode>()
     /** Map from parent tree to child trees that are about to be stitched together */
@@ -192,6 +193,8 @@
     }
 
     private fun clear() {
+        rootLocation[0] = 0
+        rootLocation[1] = 0
         cache.clear()
         inlineClassConverter.clear()
         claimedNodes.clear()
@@ -203,6 +206,7 @@
 
     @OptIn(InternalComposeApi::class)
     private fun convert(tables: Set<CompositionData>, view: View): List<InspectorNode> {
+        view.getLocationOnScreen(rootLocation)
         val trees = tables.mapNotNull { convert(it, view) }
         return when (trees.size) {
             0 -> listOf()
@@ -414,8 +418,8 @@
     @OptIn(UiToolingDataApi::class)
     private fun parsePosition(group: Group, node: MutableInspectorNode) {
         val box = group.box
-        node.top = box.top
-        node.left = box.left
+        node.top = box.top + rootLocation[1]
+        node.left = box.left + rootLocation[0]
         node.height = box.bottom - box.top
         node.width = box.right - box.left
     }
diff --git a/compose/ui/ui-test-junit4/build.gradle b/compose/ui/ui-test-junit4/build.gradle
index 1130408..1923226 100644
--- a/compose/ui/ui-test-junit4/build.gradle
+++ b/compose/ui/ui-test-junit4/build.gradle
@@ -57,8 +57,8 @@
         androidTestImplementation(ANDROIDX_TEST_RULES)
         androidTestImplementation(ANDROIDX_TEST_RUNNER)
         androidTestImplementation(TRUTH)
-        androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-        androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+        androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+        androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
         androidTestImplementation(MOCKITO_KOTLIN, {
             exclude group: "org.mockito" // to keep control on the mockito version
         })
@@ -108,8 +108,8 @@
                 implementation(ANDROIDX_TEST_RULES)
                 implementation(ANDROIDX_TEST_RUNNER)
                 implementation(TRUTH)
-                implementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-                implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+                implementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+                implementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
                 implementation(MOCKITO_KOTLIN, {
                     exclude group: "org.mockito" // to keep control on the mockito version
                 })
diff --git a/compose/ui/ui-test/build.gradle b/compose/ui/ui-test/build.gradle
index 4f251e1..19de9f8 100644
--- a/compose/ui/ui-test/build.gradle
+++ b/compose/ui/ui-test/build.gradle
@@ -52,8 +52,8 @@
         androidTestImplementation(project(":compose:test-utils"))
         androidTestImplementation(project(":compose:ui:ui-test-junit4"))
         androidTestImplementation(TRUTH)
-        androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-        androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+        androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+        androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
         androidTestImplementation(MOCKITO_KOTLIN, {
             exclude group: "org.mockito" // to keep control on the mockito version
         })
@@ -96,8 +96,8 @@
                 implementation(project(":compose:ui:ui-test-junit4"))
                 implementation(project(":activity:activity-compose"))
                 implementation(TRUTH)
-                implementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-                implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+                implementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+                implementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
                 implementation(MOCKITO_KOTLIN, {
                     exclude group: "org.mockito" // to keep control on the mockito version
                 })
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index 07f2cfb..f3bb4c5 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -66,9 +66,9 @@
         androidTestImplementation(ANDROIDX_TEST_RUNNER)
         androidTestImplementation(ESPRESSO_CORE)
         androidTestImplementation(JUNIT)
-        androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+        androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
         // DexMaker has it"s own MockMaker
-        androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+        androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
         androidTestImplementation(TRUTH)
         androidTestImplementation(MOCKITO_KOTLIN, {
             exclude group: "org.mockito" // to keep control on the mockito version
@@ -139,9 +139,9 @@
                 implementation(ANDROIDX_TEST_RUNNER)
                 implementation(ESPRESSO_CORE)
                 implementation(JUNIT)
-                implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+                implementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
                 // DexMaker has it"s own MockMaker
-                implementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+                implementation(MOCKITO_CORE, excludes.bytebuddy)
                 implementation(TRUTH)
                 implementation(MOCKITO_KOTLIN, {
                     exclude group: "org.mockito" // to keep control on the mockito version
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 1517399..80bb64e 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -86,9 +86,9 @@
         androidTestImplementation(KOTLIN_COROUTINES_TEST)
         androidTestImplementation(ESPRESSO_CORE)
         androidTestImplementation(JUNIT)
-        androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+        androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
         // DexMaker has it"s own MockMaker
-        androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+        androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
         // DexMaker has it"s own MockMaker
         androidTestImplementation(TRUTH)
         androidTestImplementation(MOCKITO_KOTLIN, {
@@ -190,9 +190,9 @@
                 implementation(KOTLIN_COROUTINES_TEST)
                 implementation(ESPRESSO_CORE)
                 implementation(JUNIT)
-                implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+                implementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
                 // DexMaker has it"s own MockMaker
-                implementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+                implementation(MOCKITO_CORE, excludes.bytebuddy)
                 // DexMaker has it"s own MockMaker
                 implementation(TRUTH)
                 implementation(MOCKITO_KOTLIN, {
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 874e013..071661a 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
@@ -111,7 +111,7 @@
             "AppWindow should be created inside AWT Event Thread (use SwingUtilities.invokeLater " +
                 "or just dsl for creating window: Window { })"
         }
-        window = ComposeWindow(parent = this)
+        window = ComposeWindow()
         window.apply {
             defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
             addWindowListener(object : WindowAdapter() {
@@ -122,7 +122,7 @@
                             onDispose?.invoke()
                             onDismiss?.invoke()
                             events.invokeOnClose()
-                            AppManager.removeWindow(parent)
+                            AppManager.removeWindow(this@AppWindow)
                         }
                     }
                 }
@@ -137,7 +137,7 @@
                 override fun windowGainedFocus(event: WindowEvent) {
                     // Dialogs should not receive a common application menu bar
                     if (invoker == null) {
-                        window.setJMenuBar(parent.menuBar?.menuBar)
+                        window.setJMenuBar([email protected]?.menuBar)
                     }
                     events.invokeOnFocusGet()
                 }
@@ -417,7 +417,6 @@
         window.setContent(parentComposition) {
             CompositionLocalProvider(
                 LocalAppWindow provides this,
-                LocalLayerContainer provides window,
                 content = content
             )
         }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt
index 90c25cf..273b3b0 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt
@@ -17,6 +17,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionContext
+import androidx.compose.runtime.CompositionLocalProvider
 import org.jetbrains.skiko.ClipComponent
 import org.jetbrains.skiko.GraphicsApi
 import java.awt.Component
@@ -26,9 +27,8 @@
 /**
  * ComposeWindow is a window for building UI using Compose for Desktop.
  * ComposeWindow inherits javax.swing.JFrame.
- * @param parent The parent AppFrame that wraps the ComposeWindow.
  */
-class ComposeWindow(val parent: AppFrame) : JFrame() {
+class ComposeWindow : JFrame() {
     private var isDisposed = false
     internal val layer = ComposeLayer()
     private val pane = object : JLayeredPane() {
@@ -73,8 +73,13 @@
     ) {
         layer.setContent(
             parentComposition = parentComposition,
-            content = content
-        )
+        ) {
+            CompositionLocalProvider(
+                LocalLayerContainer provides this
+            ) {
+                content()
+            }
+        }
     }
 
     override fun dispose() {
diff --git a/contentpager/contentpager/build.gradle b/contentpager/contentpager/build.gradle
index 33bc9f0..17c5064 100644
--- a/contentpager/contentpager/build.gradle
+++ b/contentpager/contentpager/build.gradle
@@ -34,7 +34,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
 }
 
 androidx {
diff --git a/coordinatorlayout/coordinatorlayout/build.gradle b/coordinatorlayout/coordinatorlayout/build.gradle
index 98639d0..a6a6937 100644
--- a/coordinatorlayout/coordinatorlayout/build.gradle
+++ b/coordinatorlayout/coordinatorlayout/build.gradle
@@ -18,10 +18,10 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(ESPRESSO_CONTRIB, excludes.espresso)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.coordinatorlayout", module: "coordinatorlayout"
     })
diff --git a/core/core-animation-integration-tests/testapp/build.gradle b/core/core-animation-integration-tests/testapp/build.gradle
index 5e2863f..f1a3103 100644
--- a/core/core-animation-integration-tests/testapp/build.gradle
+++ b/core/core-animation-integration-tests/testapp/build.gradle
@@ -27,7 +27,7 @@
     implementation(project(":core:core-animation"))
     implementation(project(":core:core-animation-testing"))
 
-    implementation(ANDROIDX_TEST_EXT_JUNIT, libs.exclude_for_espresso)
-    implementation(ANDROIDX_TEST_CORE, libs.exclude_for_espresso)
-    implementation(ANDROIDX_TEST_RULES, libs.exclude_for_espresso)
+    implementation(ANDROIDX_TEST_EXT_JUNIT, excludes.espresso)
+    implementation(ANDROIDX_TEST_CORE, excludes.espresso)
+    implementation(ANDROIDX_TEST_RULES, excludes.espresso)
 }
diff --git a/core/core-animation/build.gradle b/core/core-animation/build.gradle
index 2b4bbef..d7061d3 100644
--- a/core/core-animation/build.gradle
+++ b/core/core-animation/build.gradle
@@ -29,8 +29,8 @@
     implementation("androidx.core:core:1.3.1")
     implementation("androidx.collection:collection:1.1.0")
 
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT, libs.exclude_for_espresso)
-    androidTestImplementation(ANDROIDX_TEST_RULES, libs.exclude_for_espresso)
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT, excludes.espresso)
+    androidTestImplementation(ANDROIDX_TEST_RULES, excludes.espresso)
 }
 
 androidx {
diff --git a/core/core-google-shortcuts/build.gradle b/core/core-google-shortcuts/build.gradle
index 56bca79..2484a3a 100644
--- a/core/core-google-shortcuts/build.gradle
+++ b/core/core-google-shortcuts/build.gradle
@@ -47,8 +47,8 @@
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(ESPRESSO_INTENTS)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestImplementation(TRUTH)
 }
 
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 80c146b..4b127c1 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -24,15 +24,15 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(MULTIDEX)
 
     // Including both dexmakers allows support for all API levels plus final mocking support on
     // API 28+. The implementation is swapped based on the finality of the mock type. This
     // delegation is handled manually inside androidx.core.util.mockito.CustomMockMaker.
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO_INLINE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO_INLINE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0") {
         exclude group: "androidx.core", module: "core"
     }
diff --git a/core/core/lint-baseline.xml b/core/core/lint-baseline.xml
index e683e40..978eb9b 100644
--- a/core/core/lint-baseline.xml
+++ b/core/core/lint-baseline.xml
@@ -30,7 +30,7 @@
         errorLine2="    ^">
         <location
             file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="605"
+            line="602"
             column="5"/>
     </issue>
 
@@ -228,7 +228,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="553"
+            line="679"
             column="29"/>
     </issue>
 
@@ -239,7 +239,7 @@
         errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="605"
+            line="625"
             column="25"/>
     </issue>
 
@@ -2962,193 +2962,6 @@
 
     <issue
         id="ClassVerificationFailure"
-        message="This call references a method added in API level 16; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.startActivities(intents, options);"
-        errorLine2="                    ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="240"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 16; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.startActivity(intent, options);"
-        errorLine2="                    ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="267"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getDataDir();"
-        errorLine2="                           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="291"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getObbDirs();"
-        errorLine2="                           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="344"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getExternalFilesDirs(type);"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="397"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 19; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getExternalCacheDirs();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="450"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getDrawable(id);"
-        errorLine2="                           ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="471"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getColorStateList(id);"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="510"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getColor(id);"
-        errorLine2="                           ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="533"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getNoBackupFilesDir();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="574"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 21; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getCodeCacheDir();"
-        errorLine2="                           ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="598"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.createDeviceProtectedStorageContext();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="653"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.isDeviceProtectedStorage();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="667"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getMainExecutor();"
-        errorLine2="                           ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="680"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.startForegroundService(intent);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="697"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(serviceClass);"
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="717"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.ContextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemServiceName(serviceClass);"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="737"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
         message="This call references a method added in API level 28; however, the containing class androidx.core.database.CursorWindowCompat is reachable from earlier API levels and will fail run-time class verification."
         errorLine1="            return new CursorWindow(name, windowSizeBytes);"
         errorLine2="                   ~~~~~~~~~~~~~~~~">
@@ -8467,7 +8280,7 @@
         errorLine2="                       ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="95"
+            line="104"
             column="24"/>
     </issue>
 
@@ -8478,7 +8291,7 @@
         errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="128"
+            line="137"
             column="24"/>
     </issue>
 
@@ -8489,7 +8302,7 @@
         errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="130"
+            line="139"
             column="24"/>
     </issue>
 
@@ -8500,7 +8313,7 @@
         errorLine2="                       ~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="158"
+            line="167"
             column="24"/>
     </issue>
 
@@ -8511,7 +8324,7 @@
         errorLine2="                       ~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="188"
+            line="195"
             column="24"/>
     </issue>
 
@@ -9908,7 +9721,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="695"
+            line="715"
             column="22"/>
     </issue>
 
@@ -9919,7 +9732,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="704"
+            line="724"
             column="29"/>
     </issue>
 
@@ -9930,7 +9743,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="745"
+            line="765"
             column="29"/>
     </issue>
 
@@ -9941,7 +9754,7 @@
         errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="816"
+            line="836"
             column="62"/>
     </issue>
 
@@ -9952,7 +9765,7 @@
         errorLine2="                                                  ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="821"
+            line="841"
             column="51"/>
     </issue>
 
@@ -9963,7 +9776,7 @@
         errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="822"
+            line="842"
             column="58"/>
     </issue>
 
@@ -9974,7 +9787,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="843"
+            line="863"
             column="22"/>
     </issue>
 
@@ -9985,7 +9798,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="866"
+            line="886"
             column="22"/>
     </issue>
 
@@ -9996,7 +9809,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="867"
+            line="887"
             column="22"/>
     </issue>
 
@@ -10007,7 +9820,7 @@
         errorLine2="                                                                  ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="919"
+            line="939"
             column="67"/>
     </issue>
 
@@ -10018,7 +9831,7 @@
         errorLine2="                                             ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="919"
+            line="939"
             column="46"/>
     </issue>
 
@@ -10029,7 +9842,7 @@
         errorLine2="                                            ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="920"
+            line="940"
             column="45"/>
     </issue>
 
@@ -10040,7 +9853,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="935"
+            line="955"
             column="48"/>
     </issue>
 
@@ -10051,7 +9864,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="938"
+            line="958"
             column="26"/>
     </issue>
 
@@ -10062,7 +9875,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="995"
+            line="1015"
             column="22"/>
     </issue>
 
@@ -10073,7 +9886,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="1011"
+            line="1031"
             column="29"/>
     </issue>
 
@@ -10084,7 +9897,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="1029"
+            line="1049"
             column="22"/>
     </issue>
 
@@ -10095,7 +9908,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/widget/TextViewCompat.java"
-            line="1046"
+            line="1066"
             column="29"/>
     </issue>
 
@@ -11745,7 +11558,7 @@
         errorLine2="             ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="150"
+            line="156"
             column="14"/>
     </issue>
 
@@ -14626,7 +14439,7 @@
         errorLine2="                  ~~~~">
         <location
             file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="596"
+            line="593"
             column="19"/>
     </issue>
 
@@ -14637,7 +14450,7 @@
         errorLine2="                  ~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="678"
+            line="675"
             column="19"/>
     </issue>
 
@@ -14648,7 +14461,7 @@
         errorLine2="                                           ~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/ContextCompat.java"
-            line="678"
+            line="675"
             column="44"/>
     </issue>
 
@@ -18135,7 +17948,7 @@
         errorLine2="                                                     ~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="307"
+            line="433"
             column="54"/>
     </issue>
 
@@ -18146,7 +17959,7 @@
         errorLine2="                  ~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="376"
+            line="502"
             column="19"/>
     </issue>
 
@@ -18157,7 +17970,7 @@
         errorLine2="                                                                              ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
-            line="376"
+            line="502"
             column="79"/>
     </issue>
 
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 d92b30f..882f35b 100644
--- a/core/core/src/main/java/androidx/core/content/ContextCompat.java
+++ b/core/core/src/main/java/androidx/core/content/ContextCompat.java
@@ -84,11 +84,13 @@
 import android.appwidget.AppWidgetManager;
 import android.bluetooth.BluetoothManager;
 import android.content.ClipboardManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.RestrictionsManager;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 import android.hardware.ConsumerIrManager;
@@ -115,6 +117,7 @@
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.Process;
+import android.os.StatFs;
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.os.storage.StorageManager;
@@ -133,9 +136,11 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.ColorRes;
+import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.core.app.ActivityOptionsCompat;
 import androidx.core.content.res.ResourcesCompat;
 import androidx.core.os.EnvironmentCompat;
@@ -146,7 +151,7 @@
 import java.util.concurrent.Executor;
 
 /**
- * Helper for accessing features in {@link android.content.Context}.
+ * Helper for accessing features in {@link Context}.
  */
 public class ContextCompat {
     private static final String TAG = "ContextCompat";
@@ -173,7 +178,7 @@
     @Nullable
     public static String getAttributionTag(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 30) {
-            return context.getAttributionTag();
+            return Api30Impl.getAttributionTag(context);
         }
 
         return null;
@@ -232,13 +237,13 @@
      * @param intents Array of intents defining the activities that will be started. The element
      *                length-1 will correspond to the top activity on the resulting task stack.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, android.os.Bundle)}
+     *                See {@link Context#startActivity(Intent, Bundle)}
      * @return true if the underlying API was available and the call was successful, false otherwise
      */
     public static boolean startActivities(@NonNull Context context, @NonNull Intent[] intents,
             @Nullable Bundle options) {
         if (Build.VERSION.SDK_INT >= 16) {
-            context.startActivities(intents, options);
+            Api16Impl.startActivities(context, intents, options);
         } else {
             context.startActivities(intents);
         }
@@ -255,7 +260,7 @@
      * not exist the activity will be launched normally.</p>
      *
      * @param context Context to launch activity from.
-     * @param intent The description of the activity to start.
+     * @param intent  The description of the activity to start.
      * @param options Additional options for how the Activity should be started.
      *                May be null if there are no options. See
      *                {@link ActivityOptionsCompat} for how to build the Bundle
@@ -265,7 +270,7 @@
     public static void startActivity(@NonNull Context context, @NonNull Intent intent,
             @Nullable Bundle options) {
         if (Build.VERSION.SDK_INT >= 16) {
-            context.startActivity(intent, options);
+            Api16Impl.startActivity(context, intent, options);
         } else {
             context.startActivity(intent);
         }
@@ -289,7 +294,7 @@
     @Nullable
     public static File getDataDir(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 24) {
-            return context.getDataDir();
+            return Api24Impl.getDataDir(context);
         } else {
             final String dataDir = context.getApplicationInfo().dataDir;
             return dataDir != null ? new File(dataDir) : null;
@@ -318,9 +323,9 @@
      * <p>
      * An application may store data on any or all of the returned devices. For
      * example, an app may choose to store large files on the device with the
-     * most available space, as measured by {@link android.os.StatFs}.
+     * most available space, as measured by {@link StatFs}.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * Starting in {@link Build.VERSION_CODES#KITKAT}, no permissions
      * are required to write to the returned paths; they're always accessible to
      * the calling app. Before then,
      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
@@ -342,9 +347,9 @@
     @NonNull
     public static File[] getObbDirs(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 19) {
-            return context.getObbDirs();
+            return Api19Impl.getObbDirs(context);
         } else {
-            return new File[] { context.getObbDir() };
+            return new File[]{context.getObbDir()};
         }
     }
 
@@ -370,9 +375,9 @@
      * <p>
      * An application may store data on any or all of the returned devices. For
      * example, an app may choose to store large files on the device with the
-     * most available space, as measured by {@link android.os.StatFs}.
+     * most available space, as measured by {@link StatFs}.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * Starting in {@link Build.VERSION_CODES#KITKAT}, no permissions
      * are required to write to the returned paths; they're always accessible to
      * the calling app. Before then,
      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
@@ -395,9 +400,9 @@
     @NonNull
     public static File[] getExternalFilesDirs(@NonNull Context context, @Nullable String type) {
         if (Build.VERSION.SDK_INT >= 19) {
-            return context.getExternalFilesDirs(type);
+            return Api19Impl.getExternalFilesDirs(context, type);
         } else {
-            return new File[] { context.getExternalFilesDir(type) };
+            return new File[]{context.getExternalFilesDir(type)};
         }
     }
 
@@ -423,9 +428,9 @@
      * <p>
      * An application may store data on any or all of the returned devices. For
      * example, an app may choose to store large files on the device with the
-     * most available space, as measured by {@link android.os.StatFs}.
+     * most available space, as measured by {@link StatFs}.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * Starting in {@link Build.VERSION_CODES#KITKAT}, no permissions
      * are required to write to the returned paths; they're always accessible to
      * the calling app. Before then,
      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
@@ -448,16 +453,16 @@
     @NonNull
     public static File[] getExternalCacheDirs(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 19) {
-            return context.getExternalCacheDirs();
+            return Api19Impl.getExternalCacheDirs(context);
         } else {
-            return new File[] { context.getExternalCacheDir() };
+            return new File[]{context.getExternalCacheDir()};
         }
     }
 
     /**
      * Returns a drawable object associated with a particular resource ID.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the
+     * Starting in {@link Build.VERSION_CODES#LOLLIPOP}, the
      * returned drawable will be styled for the specified Context's theme.
      *
      * @param id The desired resource identifier, as generated by the aapt tool.
@@ -469,7 +474,7 @@
     @Nullable
     public static Drawable getDrawable(@NonNull Context context, @DrawableRes int id) {
         if (Build.VERSION.SDK_INT >= 21) {
-            return context.getDrawable(id);
+            return Api21Impl.getDrawable(context, id);
         } else if (Build.VERSION.SDK_INT >= 16) {
             return context.getResources().getDrawable(id);
         } else {
@@ -492,14 +497,14 @@
     /**
      * Returns a color state list associated with a particular resource ID.
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
+     * Starting in {@link Build.VERSION_CODES#M}, the returned
      * color state list will be styled for the specified Context's theme.
      *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
      * @return A color state list, or {@code null} if the resource could not be
-     *         resolved.
+     * resolved.
      * @throws android.content.res.Resources.NotFoundException if the given ID
      *         does not exist.
      */
@@ -511,7 +516,7 @@
     /**
      * Returns a color associated with a particular resource ID
      * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
+     * Starting in {@link Build.VERSION_CODES#M}, the returned
      * color will be styled for the specified Context's theme.
      *
      * @param id The desired resource identifier, as generated by the aapt
@@ -525,7 +530,7 @@
     @ColorInt
     public static int getColor(@NonNull Context context, @ColorRes int id) {
         if (Build.VERSION.SDK_INT >= 23) {
-            return context.getColor(id);
+            return Api23Impl.getColor(context, id);
         } else {
             return context.getResources().getColor(id);
         }
@@ -535,38 +540,35 @@
      * Determine whether <em>you</em> have been granted a particular permission.
      *
      * @param permission The name of the permission being checked.
-     *
-     * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
-     * permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
-     *
-     * @see android.content.pm.PackageManager#checkPermission(String, String)
+     * @return {@link PackageManager#PERMISSION_GRANTED} if you have the
+     * permission, or {@link PackageManager#PERMISSION_DENIED} if not.
+     * @see PackageManager#checkPermission(String, String)
      */
     public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
         if (permission == null) {
             throw new IllegalArgumentException("permission is null");
         }
 
-        return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
+        return context.checkPermission(permission, Process.myPid(), Process.myUid());
     }
 
     /**
      * Returns the absolute path to the directory on the filesystem similar to
      * {@link Context#getFilesDir()}.  The difference is that files placed under this
      * directory will be excluded from automatic backup to remote storage on
-     * devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later.
+     * devices running {@link Build.VERSION_CODES#LOLLIPOP} or later.
      *
      * <p>No permissions are required to read or write to the returned path, since this
      * path is internal storage.
      *
      * @return The path of the directory holding application files that will not be
-     *         automatically backed up to remote storage.
-     *
-     * @see android.content.Context#getFilesDir()
+     * automatically backed up to remote storage.
+     * @see Context#getFilesDir()
      */
     @Nullable
     public static File getNoBackupFilesDir(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 21) {
-            return context.getNoBackupFilesDir();
+            return Api21Impl.getNoBackupFilesDir(context);
         } else {
             ApplicationInfo appInfo = context.getApplicationInfo();
             return createFilesDir(new File(appInfo.dataDir, "no_backup"));
@@ -576,7 +578,7 @@
     /**
      * Returns the absolute path to the application specific cache directory on
      * the filesystem designed for storing cached code. On devices running
-     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later, the system will delete
+     * {@link Build.VERSION_CODES#LOLLIPOP} or later, the system will delete
      * any files stored in this location both when your specific application is
      * upgraded, and when the entire platform is upgraded.
      * <p>
@@ -590,7 +592,7 @@
      */
     public static File getCodeCacheDir(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 21) {
-            return context.getCodeCacheDir();
+            return Api21Impl.getCodeCacheDir(context);
         } else {
             ApplicationInfo appInfo = context.getApplicationInfo();
             return createFilesDir(new File(appInfo.dataDir, "code_cache"));
@@ -645,7 +647,7 @@
     @Nullable
     public static Context createDeviceProtectedStorageContext(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 24) {
-            return context.createDeviceProtectedStorageContext();
+            return Api24Impl.createDeviceProtectedStorageContext(context);
         } else {
             return null;
         }
@@ -659,7 +661,7 @@
      */
     public static boolean isDeviceProtectedStorage(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 24) {
-            return context.isDeviceProtectedStorage();
+            return Api24Impl.isDeviceProtectedStorage(context);
         } else {
             return false;
         }
@@ -672,7 +674,7 @@
      */
     public static Executor getMainExecutor(Context context) {
         if (Build.VERSION.SDK_INT >= 28) {
-            return context.getMainExecutor();
+            return Api28Impl.getMainExecutor(context);
         }
         return ExecutorCompat.create(new Handler(context.getMainLooper()));
     }
@@ -682,14 +684,13 @@
      * for before O.
      *
      * @param context Context to start Service from.
-     * @param intent The description of the Service to start.
-     *
+     * @param intent  The description of the Service to start.
      * @see Context#startForegroundService(Intent)
      * @see Context#startService(Intent)
      */
     public static void startForegroundService(@NonNull Context context, @NonNull Intent intent) {
         if (Build.VERSION.SDK_INT >= 26) {
-            context.startForegroundService(intent);
+            Api26Impl.startForegroundService(context, intent);
         } else {
             // Pre-O behavior.
             context.startService(intent);
@@ -699,17 +700,16 @@
     /**
      * Return the handle to a system-level service by class.
      *
-     * @param context Context to retrieve service from.
+     * @param context      Context to retrieve service from.
      * @param serviceClass The class of the desired service.
      * @return The service or null if the class is not a supported system service.
-     *
      * @see Context#getSystemService(Class)
      */
     @SuppressWarnings("unchecked")
     @Nullable
     public static <T> T getSystemService(@NonNull Context context, @NonNull Class<T> serviceClass) {
         if (Build.VERSION.SDK_INT >= 23) {
-            return context.getSystemService(serviceClass);
+            return Api23Impl.getSystemService(context, serviceClass);
         }
 
         String serviceName = getSystemServiceName(context, serviceClass);
@@ -719,17 +719,16 @@
     /**
      * Gets the name of the system-level service that is represented by the specified class.
      *
-     * @param context Context to retrieve service name from.
+     * @param context      Context to retrieve service name from.
      * @param serviceClass The class of the desired service.
      * @return The service name or null if the class is not a supported system service.
-     *
      * @see Context#getSystemServiceName(Class)
      */
     @Nullable
     public static String getSystemServiceName(@NonNull Context context,
             @NonNull Class<?> serviceClass) {
         if (Build.VERSION.SDK_INT >= 23) {
-            return context.getSystemServiceName(serviceClass);
+            return Api23Impl.getSystemServiceName(context, serviceClass);
         }
         return LegacyServiceMapHolder.SERVICES.get(serviceClass);
     }
@@ -804,4 +803,150 @@
             SERVICES.put(WindowManager.class, WINDOW_SERVICE);
         }
     }
+
+    @RequiresApi(16)
+    static class Api16Impl {
+        private Api16Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static void startActivities(Context obj, Intent[] intents, Bundle options) {
+            obj.startActivities(intents, options);
+        }
+
+        @DoNotInline
+        static void startActivity(Context obj, Intent intent, Bundle options) {
+            obj.startActivity(intent, options);
+        }
+    }
+
+    @RequiresApi(19)
+    static class Api19Impl {
+        private Api19Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static File[] getExternalCacheDirs(Context obj) {
+            return obj.getExternalCacheDirs();
+        }
+
+        @DoNotInline
+        static File[] getExternalFilesDirs(Context obj, String type) {
+            return obj.getExternalFilesDirs(type);
+        }
+
+        @DoNotInline
+        static File[] getObbDirs(Context obj) {
+            return obj.getObbDirs();
+        }
+    }
+
+    @RequiresApi(21)
+    static class Api21Impl {
+        private Api21Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static Drawable getDrawable(Context obj, int id) {
+            return obj.getDrawable(id);
+        }
+
+        @DoNotInline
+        static File getNoBackupFilesDir(Context obj) {
+            return obj.getNoBackupFilesDir();
+        }
+
+        @DoNotInline
+        static File getCodeCacheDir(Context obj) {
+            return obj.getCodeCacheDir();
+        }
+    }
+
+    @RequiresApi(23)
+    static class Api23Impl {
+        private Api23Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static ColorStateList getColorStateList(Context obj, int id) {
+            return obj.getColorStateList(id);
+        }
+
+        @DoNotInline
+        static int getColor(Context obj, int id) {
+            return obj.getColor(id);
+        }
+
+        @DoNotInline
+        static <T> T getSystemService(Context obj, Class<T> serviceClass) {
+            return obj.getSystemService(serviceClass);
+        }
+
+        @DoNotInline
+        static String getSystemServiceName(Context obj, Class<?> serviceClass) {
+            return obj.getSystemServiceName(serviceClass);
+        }
+    }
+
+    @RequiresApi(24)
+    static class Api24Impl {
+        private Api24Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static File getDataDir(Context obj) {
+            return obj.getDataDir();
+        }
+
+        @DoNotInline
+        static Context createDeviceProtectedStorageContext(Context obj) {
+            return obj.createDeviceProtectedStorageContext();
+        }
+
+        @DoNotInline
+        static boolean isDeviceProtectedStorage(Context obj) {
+            return obj.isDeviceProtectedStorage();
+        }
+    }
+
+    @RequiresApi(26)
+    static class Api26Impl {
+        private Api26Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static ComponentName startForegroundService(Context obj, Intent service) {
+            return obj.startForegroundService(service);
+        }
+    }
+
+    @RequiresApi(28)
+    static class Api28Impl {
+        private Api28Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static Executor getMainExecutor(Context obj) {
+            return obj.getMainExecutor();
+        }
+    }
+
+    @RequiresApi(30)
+    static class Api30Impl {
+        private Api30Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static String getAttributionTag(Context obj) {
+            return obj.getAttributionTag();
+        }
+    }
 }
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 02d58fa..aec0e23 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -118,6 +118,10 @@
     docs(project(":emoji"))
     docs(project(":emoji-appcompat"))
     docs(project(":emoji-bundled"))
+    docs(project(":emoji2:emoji2"))
+    docs(project(":emoji2:emoji2-bundled"))
+    docs(project(":emoji2:emoji2-views"))
+    docs(project(":emoji2:emoji2-views-helper"))
     docs(project(":enterprise-feedback"))
     docs(project(":enterprise-feedback-testing"))
     docs(project(":exifinterface:exifinterface"))
diff --git a/dynamic-animation/dynamic-animation-ktx/build.gradle b/dynamic-animation/dynamic-animation-ktx/build.gradle
index 9533a73..e3ec3f0 100644
--- a/dynamic-animation/dynamic-animation-ktx/build.gradle
+++ b/dynamic-animation/dynamic-animation-ktx/build.gradle
@@ -35,9 +35,9 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
 
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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
 }
 
 androidx {
diff --git a/dynamic-animation/dynamic-animation/build.gradle b/dynamic-animation/dynamic-animation/build.gradle
index 6ad3e31..13e952f 100644
--- a/dynamic-animation/dynamic-animation/build.gradle
+++ b/dynamic-animation/dynamic-animation/build.gradle
@@ -16,9 +16,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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
 }
 
 androidx {
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index 2c54100..94334a7 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -31,9 +31,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-runtime"))
 }
 
diff --git a/emoji2/emoji2-benchmark/build.gradle b/emoji2/emoji2-benchmark/build.gradle
index c3dd0be..a4709c1 100644
--- a/emoji2/emoji2-benchmark/build.gradle
+++ b/emoji2/emoji2-benchmark/build.gradle
@@ -34,8 +34,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation project(':internal-testutils-runtime')
     androidTestImplementation(KOTLIN_STDLIB)
 }
@@ -43,7 +43,7 @@
 androidx {
     name = "Emoji2 Benchmarks"
     publish = Publish.NONE
-    mavenGroup = LibraryGroups.NAVIGATION
-    inceptionYear = "2018"
-    description = "Navigation Benchmarks"
+    mavenGroup = LibraryGroups.EMOJI2
+    inceptionYear = "2021"
+    description = "Emoji2 Benchmarks"
 }
diff --git a/emoji2/emoji2-bundled/api/current.txt b/emoji2/emoji2-bundled/api/current.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/current.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-bundled/api/public_plus_experimental_current.txt b/emoji2/emoji2-bundled/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/public_plus_experimental_current.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-bundled/api/res-current.txt b/emoji2/emoji2-bundled/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/res-current.txt
diff --git a/emoji2/emoji2-bundled/api/restricted_current.txt b/emoji2/emoji2-bundled/api/restricted_current.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/restricted_current.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-bundled/build.gradle b/emoji2/emoji2-bundled/build.gradle
index 0978cbf..7d6c347 100644
--- a/emoji2/emoji2-bundled/build.gradle
+++ b/emoji2/emoji2-bundled/build.gradle
@@ -39,9 +39,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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 project(':internal-testutils-runtime')
 
     // view tests that use font are in this module as well; for licensing reasons
@@ -50,11 +50,12 @@
 
 androidx {
     name = "Android Emoji2 Compat"
-    publish = Publish.NONE
+    publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.EMOJI2
     mavenGroup = LibraryGroups.EMOJI2
     inceptionYear = "2017"
-    description = "Library bundled with assets to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters."
+    description = "Library bundled with assets to enable emoji compatibility in Kitkat and newer " +
+            "devices to avoid the empty emoji characters."
 
     license {
         name = "SIL Open Font License, Version 1.1"
diff --git a/emoji2/emoji2-views-helper/AndroidManifest.xml b/emoji2/emoji2-views-helper/AndroidManifest.xml
index 2f4b90a5..0b6d86b 100644
--- a/emoji2/emoji2-views-helper/AndroidManifest.xml
+++ b/emoji2/emoji2-views-helper/AndroidManifest.xml
@@ -13,4 +13,4 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest package="androidx.emoji2.helpers"/>
+<manifest package="androidx.emoji2.viewshelper"/>
diff --git a/emoji2/emoji2-views-helper/api/current.txt b/emoji2/emoji2-views-helper/api/current.txt
new file mode 100644
index 0000000..e4cacba9
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/current.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.emoji2.viewshelper {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public int getMaxEmojiCount();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/api/public_plus_experimental_current.txt b/emoji2/emoji2-views-helper/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..e4cacba9
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/public_plus_experimental_current.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.emoji2.viewshelper {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public int getMaxEmojiCount();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/api/res-current.txt b/emoji2/emoji2-views-helper/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/res-current.txt
diff --git a/emoji2/emoji2-views-helper/api/restricted_current.txt b/emoji2/emoji2-views-helper/api/restricted_current.txt
new file mode 100644
index 0000000..e4cacba9
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/restricted_current.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.emoji2.viewshelper {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public int getMaxEmojiCount();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/build.gradle b/emoji2/emoji2-views-helper/build.gradle
index 9f5aef9..038f23c 100644
--- a/emoji2/emoji2-views-helper/build.gradle
+++ b/emoji2/emoji2-views-helper/build.gradle
@@ -20,9 +20,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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 project(':internal-testutils-runtime')
 }
 
@@ -37,7 +37,7 @@
 
 androidx {
     name = "Android Emoji2 Compat view helpers"
-    publish = Publish.NONE
+    publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.EMOJI2
     mavenGroup = LibraryGroups.EMOJI2
     inceptionYear = "2017"
diff --git a/emoji2/emoji2-views-helper/src/androidTest/AndroidManifest.xml b/emoji2/emoji2-views-helper/src/androidTest/AndroidManifest.xml
index 6c30771..f28fe19 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/AndroidManifest.xml
+++ b/emoji2/emoji2-views-helper/src/androidTest/AndroidManifest.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest package="androidx.emoji2.helpers">
+<manifest package="androidx.emoji2.viewshelper">
 
     <application>
     </application>
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextViewHelperTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextViewHelperTest.java
deleted file mode 100644
index 51b71cf..0000000
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextViewHelperTest.java
+++ /dev/null
@@ -1,166 +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.emoji2.helpers;
-
-import static org.hamcrest.CoreMatchers.hasItem;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-
-import android.text.InputFilter;
-import android.text.method.PasswordTransformationMethod;
-import android.text.method.TransformationMethod;
-import android.widget.TextView;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.filters.SmallTest;
-
-import org.hamcrest.CoreMatchers;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 19)
-public class EmojiTextViewHelperTest {
-    EmojiTextViewHelper mTextViewHelper;
-    TextView mTextView;
-
-    @Before
-    public void setup() {
-        mTextView = new TextView(ApplicationProvider.getApplicationContext());
-        mTextViewHelper = new EmojiTextViewHelper(mTextView);
-    }
-
-    @Test
-    public void testUpdateTransformationMethod() {
-        mTextView.setTransformationMethod(mock(TransformationMethod.class));
-
-        mTextViewHelper.updateTransformationMethod();
-
-        assertThat(mTextView.getTransformationMethod(),
-                instanceOf(EmojiTransformationMethod.class));
-    }
-
-    @Test
-    public void testUpdateTransformationMethod_doesNotUpdateForPasswordTransformation() {
-        final PasswordTransformationMethod transformationMethod =
-                new PasswordTransformationMethod();
-        mTextView.setTransformationMethod(transformationMethod);
-
-        mTextViewHelper.updateTransformationMethod();
-
-        assertEquals(transformationMethod, mTextView.getTransformationMethod());
-    }
-
-    @Test
-    public void testUpdateTransformationMethod_doesNotCreateNewInstance() {
-        mTextView.setTransformationMethod(mock(TransformationMethod.class));
-
-        mTextViewHelper.updateTransformationMethod();
-        final TransformationMethod tm = mTextView.getTransformationMethod();
-        assertThat(tm, instanceOf(EmojiTransformationMethod.class));
-
-        // call the function again
-        mTextViewHelper.updateTransformationMethod();
-        assertSame(tm, mTextView.getTransformationMethod());
-    }
-
-    @Test
-    public void testGetFilters() {
-        final InputFilter existingFilter = mock(InputFilter.class);
-        final InputFilter[] filters = new InputFilter[]{existingFilter};
-
-        final InputFilter[] newFilters = mTextViewHelper.getFilters(filters);
-
-        assertEquals(2, newFilters.length);
-        assertThat(Arrays.asList(newFilters), hasItem(existingFilter));
-        assertNotNull(findEmojiInputFilter(newFilters));
-    }
-
-    @Test
-    public void testGetFilters_doesNotAddSecondInstance() {
-        final InputFilter existingFilter = mock(InputFilter.class);
-        final InputFilter[] filters = new InputFilter[]{existingFilter};
-
-        InputFilter[] newFilters = mTextViewHelper.getFilters(filters);
-        EmojiInputFilter emojiInputFilter = findEmojiInputFilter(newFilters);
-        assertNotNull(emojiInputFilter);
-
-        // run it again with the updated filters and see that it does not add new filter
-        newFilters = mTextViewHelper.getFilters(newFilters);
-
-        assertEquals(2, newFilters.length);
-        assertThat(Arrays.asList(newFilters), hasItem(existingFilter));
-        assertThat(Arrays.asList(newFilters), CoreMatchers.hasItem(emojiInputFilter));
-    }
-
-    private EmojiInputFilter findEmojiInputFilter(final InputFilter[] filters) {
-        for (int i = 0; i < filters.length; i++) {
-            if (filters[i] instanceof EmojiInputFilter) {
-                return (EmojiInputFilter) filters[i];
-            }
-        }
-        return null;
-    }
-
-    @Test
-    public void testWrapTransformationMethod() {
-        assertThat(mTextViewHelper.wrapTransformationMethod(null),
-                instanceOf(EmojiTransformationMethod.class));
-    }
-
-    @Test
-    public void testWrapTransformationMethod_doesNotCreateNewInstance() {
-        final TransformationMethod tm1 = mTextViewHelper.wrapTransformationMethod(null);
-        final TransformationMethod tm2 = mTextViewHelper.wrapTransformationMethod(tm1);
-        assertSame(tm1, tm2);
-    }
-
-    @Test
-    public void testSetAllCaps_withTrueSetsTransformationMethod() {
-        mTextView.setTransformationMethod(mock(TransformationMethod.class));
-        mTextViewHelper.setAllCaps(true);
-        assertThat(mTextView.getTransformationMethod(),
-                instanceOf(EmojiTransformationMethod.class));
-    }
-
-    @Test
-    public void testSetAllCaps_withFalseDoesNotSetTransformationMethod() {
-        mTextView.setTransformationMethod(null);
-        mTextViewHelper.setAllCaps(false);
-        assertNull(mTextView.getTransformationMethod());
-    }
-
-    @Test
-    public void testSetAllCaps_withPasswordTransformationDoesNotSetTransformationMethod() {
-        final PasswordTransformationMethod transformationMethod =
-                new PasswordTransformationMethod();
-        mTextView.setTransformationMethod(transformationMethod);
-        mTextViewHelper.setAllCaps(true);
-        assertSame(transformationMethod, mTextView.getTransformationMethod());
-    }
-}
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditTextHelperPre19Test.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditTextHelperPre19Test.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditTextHelperPre19Test.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditTextHelperPre19Test.java
index c431518..829fae8 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditTextHelperPre19Test.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditTextHelperPre19Test.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditTextHelperTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditTextHelperTest.java
similarity index 99%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditTextHelperTest.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditTextHelperTest.java
index c054c74..6cad221 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditTextHelperTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditTextHelperTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.assertEquals;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditableFactoryTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditableFactoryTest.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditableFactoryTest.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditableFactoryTest.java
index 36200b0..d85e40c 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiEditableFactoryTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiEditableFactoryTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.hamcrest.Matchers.arrayWithSize;
 import static org.hamcrest.Matchers.instanceOf;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiInputConnectionTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiInputConnectionTest.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiInputConnectionTest.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiInputConnectionTest.java
index 9516a10..843a803 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiInputConnectionTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiInputConnectionTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiInputFilterTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiInputFilterTest.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiInputFilterTest.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiInputFilterTest.java
index 1b75ce9..4dd7387 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiInputFilterTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiInputFilterTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiKeyListenerTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiKeyListenerTest.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiKeyListenerTest.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiKeyListenerTest.java
index d1eea0e..d944658 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiKeyListenerTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiKeyListenerTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextViewHelperPre19Test.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextViewHelperPre19Test.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextViewHelperPre19Test.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextViewHelperPre19Test.java
index 6a9932a..308dcfd 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextViewHelperPre19Test.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextViewHelperPre19Test.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextViewHelperTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextViewHelperTest.java
new file mode 100644
index 0000000..909ca57
--- /dev/null
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextViewHelperTest.java
@@ -0,0 +1,381 @@
+/*
+ * 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.emoji2.viewshelper;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.text.InputFilter;
+import android.text.method.PasswordTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.widget.TextView;
+
+import androidx.emoji2.text.EmojiCompat;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 19)
+public class EmojiTextViewHelperTest {
+    EmojiTextViewHelper mTextViewHelper;
+    TextView mTextView;
+
+    @Before
+    public void setup() {
+        mTextView = new TextView(ApplicationProvider.getApplicationContext());
+        mTextViewHelper = new EmojiTextViewHelper(mTextView);
+        EmojiCompat.reset(mock(EmojiCompat.class));
+    }
+
+    @Test
+    public void testUpdateTransformationMethod() {
+        mTextView.setTransformationMethod(mock(TransformationMethod.class));
+
+        mTextViewHelper.updateTransformationMethod();
+
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void testUpdateTransformationMethod_whenDisabled_doesntWrap() {
+        TransformationMethod mockTransform = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(mockTransform);
+
+        mTextViewHelper.setEnabled(false);
+        mTextViewHelper.updateTransformationMethod();
+
+        assertThat(mTextView.getTransformationMethod(), is(mockTransform));
+    }
+
+    @Test
+    public void testUpdateTransformation_whenNotConfigured_NoEffectWhenSkipConfig() {
+        EmojiCompat.reset((EmojiCompat) null);
+        mTextViewHelper = new EmojiTextViewHelper(mTextView, false);
+        TransformationMethod mockTransform = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(mockTransform);
+        mTextViewHelper.updateTransformationMethod();
+        assertThat(mTextView.getTransformationMethod(), is(mockTransform));
+    }
+
+    @Test
+    public void testUpdateTransformationMethod_doesNotUpdateForPasswordTransformation() {
+        final PasswordTransformationMethod transformationMethod =
+                new PasswordTransformationMethod();
+        mTextView.setTransformationMethod(transformationMethod);
+
+        mTextViewHelper.updateTransformationMethod();
+
+        assertEquals(transformationMethod, mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void testUpdateTransformationMethod_doesNotCreateNewInstance() {
+        mTextView.setTransformationMethod(mock(TransformationMethod.class));
+
+        mTextViewHelper.updateTransformationMethod();
+        final TransformationMethod tm = mTextView.getTransformationMethod();
+        assertThat(tm, instanceOf(EmojiTransformationMethod.class));
+
+        // call the function again
+        mTextViewHelper.updateTransformationMethod();
+        assertSame(tm, mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void testGetFilters() {
+        final InputFilter existingFilter = mock(InputFilter.class);
+        final InputFilter[] filters = new InputFilter[]{existingFilter};
+
+        final InputFilter[] newFilters = mTextViewHelper.getFilters(filters);
+
+        assertEquals(2, newFilters.length);
+        assertThat(Arrays.asList(newFilters), hasItem(existingFilter));
+        assertNotNull(findEmojiInputFilter(newFilters));
+    }
+
+    @Test
+    public void testGetFilters_doesNotAddSecondInstance() {
+        final InputFilter existingFilter = mock(InputFilter.class);
+        final InputFilter[] filters = new InputFilter[]{existingFilter};
+
+        InputFilter[] newFilters = mTextViewHelper.getFilters(filters);
+        EmojiInputFilter emojiInputFilter = findEmojiInputFilter(newFilters);
+        assertNotNull(emojiInputFilter);
+
+        // run it again with the updated filters and see that it does not add new filter
+        newFilters = mTextViewHelper.getFilters(newFilters);
+
+        assertEquals(2, newFilters.length);
+        assertThat(Arrays.asList(newFilters), hasItem(existingFilter));
+        assertThat(Arrays.asList(newFilters), CoreMatchers.hasItem(emojiInputFilter));
+    }
+
+    @Test
+    public void getFilter_addsFilter_whenEmptyArray() {
+        InputFilter[] actual = mTextViewHelper.getFilters(new InputFilter[0]);
+        EmojiInputFilter emojiInputFilter = findEmojiInputFilter(actual);
+        assertNotNull(emojiInputFilter);
+    }
+
+    @Test
+    public void getFilter_doesntAddFilter_whenDisabled() {
+        mTextViewHelper.setEnabled(false);
+        InputFilter[] expected = new InputFilter[0];
+        InputFilter[] actual = mTextViewHelper.getFilters(expected);
+        assertThat(expected, is(actual));
+    }
+
+    @Test
+    public void setFilter_doesntAddFilter_whenNotConfiguredAndSkipping() {
+        EmojiCompat.reset((EmojiCompat) null);
+        mTextViewHelper = new EmojiTextViewHelper(mTextView, false);
+        InputFilter[] expected = new InputFilter[0];
+        InputFilter[] actual = mTextViewHelper.getFilters(expected);
+        assertThat(expected, is(actual));
+    }
+
+    @Test
+    public void getFilter_removesFilter_whenDisabled() {
+        mTextViewHelper.setEnabled(false);
+        InputFilter[] input = new InputFilter[1];
+        input[0] = new EmojiInputFilter(mTextView);
+        InputFilter[] actual = mTextViewHelper.getFilters(input);
+        assertThat(actual, equalTo(new InputFilter[0]));
+    }
+
+    @Test
+    public void getFilter_removesAllFilters_whenDisabled() {
+        mTextViewHelper.setEnabled(false);
+        InputFilter[] input = new InputFilter[3];
+        input[0] = new EmojiInputFilter(mTextView);
+        input[1] = mock(InputFilter.class);
+        input[2] = new EmojiInputFilter(mTextView);
+        InputFilter[] actual = mTextViewHelper.getFilters(input);
+        InputFilter[] expected = new InputFilter[1];
+        expected[0] = input[1];
+        assertThat(actual, equalTo(expected));
+    }
+
+    private EmojiInputFilter findEmojiInputFilter(final InputFilter[] filters) {
+        for (int i = 0; i < filters.length; i++) {
+            if (filters[i] instanceof EmojiInputFilter) {
+                return (EmojiInputFilter) filters[i];
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void testWrapTransformationMethod_doesNotCreateNewInstance() {
+        final TransformationMethod tm1 = mTextViewHelper.wrapTransformationMethod(null);
+        final TransformationMethod tm2 = mTextViewHelper.wrapTransformationMethod(tm1);
+        assertSame(tm1, tm2);
+    }
+
+    @Test
+    public void wrapTransformationMethod_whenEnabled_andPassword_returnsOriginal() {
+        TransformationMethod expected = new PasswordTransformationMethod();
+        TransformationMethod actual = mTextViewHelper.wrapTransformationMethod(expected);
+        assertThat(actual, is(expected));
+    }
+
+    @Test
+    public void wrapTransformationMethod_whenDisabled_andPassword_returnsOriginal() {
+        mTextViewHelper.setEnabled(false);
+        TransformationMethod expected = new PasswordTransformationMethod();
+        TransformationMethod actual = mTextViewHelper.wrapTransformationMethod(expected);
+        assertThat(actual, is(expected));
+    }
+
+    @Test
+    public void wrapTransformationMethod_whenEnabled_andEmoji_returnsOriginal() {
+        TransformationMethod expected = new EmojiTransformationMethod(null);
+        TransformationMethod actual = mTextViewHelper.wrapTransformationMethod(expected);
+        assertThat(actual, is(expected));
+    }
+
+    @Test
+    public void wrapTransformationMethod_whenDisabled_andEmoji_returnsUnwrapped() {
+        TransformationMethod expected = mock(TransformationMethod.class);
+        TransformationMethod input = new EmojiTransformationMethod(expected);
+        mTextViewHelper.setEnabled(false);
+        TransformationMethod actual = mTextViewHelper.wrapTransformationMethod(input);
+        assertThat(actual, is(expected));
+    }
+
+    @Test
+    public void wrapTransformationMethod_whenNull_andEnabled_returnsEmojiTransformationMethod() {
+        TransformationMethod actual = mTextViewHelper.wrapTransformationMethod(null);
+        assertThat(actual, instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void wrapTransformationMethod_whenNull_andDisabled_returnsNull() {
+        mTextViewHelper.setEnabled(false);
+        TransformationMethod actual = mTextViewHelper.wrapTransformationMethod(null);
+        assertNull(actual);
+    }
+
+    @Test
+    public void wrapTransformationMethod_whenNotEmoji_andDisabled_returnsOriginal() {
+        mTextViewHelper.setEnabled(false);
+        TransformationMethod expected = mock(TransformationMethod.class);
+        TransformationMethod actual = mTextViewHelper.wrapTransformationMethod(expected);
+        assertThat(actual, is(expected));
+    }
+
+    @Test
+    public void testSetAllCaps_withTrueSetsTransformationMethod() {
+        mTextView.setTransformationMethod(mock(TransformationMethod.class));
+        mTextViewHelper.setAllCaps(true);
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void testSetAllCaps_withFalseDoesNotSetTransformationMethod() {
+        mTextView.setTransformationMethod(null);
+        mTextViewHelper.setAllCaps(false);
+        assertNull(mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void testSetAllCaps_withPasswordTransformationDoesNotSetTransformationMethod() {
+        final PasswordTransformationMethod transformationMethod =
+                new PasswordTransformationMethod();
+        mTextView.setTransformationMethod(transformationMethod);
+        mTextViewHelper.setAllCaps(true);
+        assertSame(transformationMethod, mTextView.getTransformationMethod());
+    }
+
+    @Test
+    public void setEnabled_fromEnabled_leavesTransformationMethod() {
+        TransformationMethod mockTransform = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(mockTransform);
+        mTextViewHelper.updateTransformationMethod();
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+        mTextViewHelper.setEnabled(true);
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void setEnabled_fromEnabled_leavesFilter() {
+        InputFilter[] expected = mTextViewHelper.getFilters(new InputFilter[0]);
+        mTextView.setFilters(expected);
+        mTextViewHelper.setEnabled(true);
+        assertThat(mTextView.getFilters(), is(expected));
+    }
+
+    @Test
+    public void setDisabled_fromEnabled_unWrapsTransformationMethod() {
+        TransformationMethod mockTransform = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(mockTransform);
+        mTextViewHelper.updateTransformationMethod();
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+
+        mTextViewHelper.setEnabled(false);
+        assertThat(mTextView.getTransformationMethod(), is(mockTransform));
+    }
+
+    @Test
+    public void setDisabled_fromEnabled_removesFilter() {
+        InputFilter[] expected = new InputFilter[0];
+        mTextView.setFilters(mTextViewHelper.getFilters(expected));
+        mTextViewHelper.setEnabled(false);
+        assertThat(mTextView.getFilters(), equalTo(expected));
+    }
+
+    @Test
+    public void setEnabled_whenEnabling_reWrapsTransformationMethod() {
+        TransformationMethod mockTransform = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(mockTransform);
+        mTextViewHelper.updateTransformationMethod();
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+
+        mTextViewHelper.setEnabled(false);
+        assertThat(mTextView.getTransformationMethod(), is(mockTransform));
+
+        mTextViewHelper.setEnabled(true);
+        assertThat(mTextView.getTransformationMethod(),
+                instanceOf(EmojiTransformationMethod.class));
+    }
+
+    @Test
+    public void setEnabled_whenEnabling_reAddsFilter() {
+        InputFilter[] expected = mTextViewHelper.getFilters(new InputFilter[0]);
+        mTextView.setFilters(expected);
+        mTextViewHelper.setEnabled(false);
+        assertThat(mTextView.getFilters(), equalTo(new InputFilter[0]));
+        mTextViewHelper.setEnabled(true);
+        assertThat(mTextView.getFilters(), equalTo(expected));
+    }
+
+    @Test
+    public void setEnabled_whenNotConfigured_andSkipConfig_doesNothing() {
+        EmojiCompat.reset((EmojiCompat) null);
+        mTextViewHelper = new EmojiTextViewHelper(mTextView, false);
+
+        InputFilter[] expectedInput = new InputFilter[1];
+        expectedInput[0] = mock(InputFilter.class);
+        mTextView.setFilters(expectedInput);
+        TransformationMethod expectedTransform = mock(TransformationMethod.class);
+        mTextView.setTransformationMethod(expectedTransform);
+
+        mTextViewHelper.setEnabled(true);
+        InputFilter[] actualInput1 = mTextView.getFilters();
+        TransformationMethod actualTransform1 = mTextView.getTransformationMethod();
+        mTextViewHelper.setEnabled(false);
+        InputFilter[] actualInput2 = mTextView.getFilters();
+        TransformationMethod actualTransform2 = mTextView.getTransformationMethod();
+        mTextViewHelper.setEnabled(true);
+        InputFilter[] actualInput3 = mTextView.getFilters();
+        TransformationMethod actualTransform3 = mTextView.getTransformationMethod();
+
+        // use transitive reference equality to do a bunch of assertions
+        assertSame(expectedInput, actualInput1);
+        assertSame(actualInput1, actualInput2);
+        assertSame(actualInput2, actualInput3);
+
+        assertSame(expectedTransform, actualTransform1);
+        assertSame(actualTransform1, actualTransform2);
+        assertSame(actualTransform2, actualTransform3);
+    }
+
+}
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextWatcherTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextWatcherTest.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextWatcherTest.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextWatcherTest.java
index e1cf65e..df1f2de 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTextWatcherTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTextWatcherTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
diff --git a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTransformationMethodTest.java b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTransformationMethodTest.java
similarity index 99%
rename from emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTransformationMethodTest.java
rename to emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTransformationMethodTest.java
index 6b0c88b..e0c6792 100644
--- a/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/helpers/EmojiTransformationMethodTest.java
+++ b/emoji2/emoji2-views-helper/src/androidTest/java/androidx/emoji2/viewshelper/EmojiTransformationMethodTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import static androidx.emoji2.util.EmojiMatcher.sameCharSequence;
 
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTextViewHelper.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTextViewHelper.java
deleted file mode 100644
index ee45e85..0000000
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTextViewHelper.java
+++ /dev/null
@@ -1,197 +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.emoji2.helpers;
-
-import android.os.Build;
-import android.text.InputFilter;
-import android.text.method.PasswordTransformationMethod;
-import android.text.method.TransformationMethod;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.core.util.Preconditions;
-import androidx.emoji2.text.EmojiCompat;
-
-/**
- * Utility class to enhance custom TextView widgets with {@link EmojiCompat}.
- * <pre>
- * public class MyEmojiTextView extends TextView {
- *     public MyEmojiTextView(Context context) {
- *         super(context);
- *         init();
- *     }
- *     // ..
- *     private void init() {
- *         getEmojiTextViewHelper().updateTransformationMethod();
- *     }
- *
- *     {@literal @}Override
- *     public void setFilters(InputFilter[] filters) {
- *         super.setFilters(getEmojiTextViewHelper().getFilters(filters));
- *     }
- *
- *     {@literal @}Override
- *     public void setAllCaps(boolean allCaps) {
- *         super.setAllCaps(allCaps);
- *         getEmojiTextViewHelper().setAllCaps(allCaps);
- *     }
- *
- *     private EmojiTextViewHelper getEmojiTextViewHelper() {
- *         if (mEmojiTextViewHelper == null) {
- *             mEmojiTextViewHelper = new EmojiTextViewHelper(this);
- *         }
- *         return mEmojiTextViewHelper;
- *     }
- * }
- * </pre>
- */
-public final class EmojiTextViewHelper {
-
-    private final HelperInternal mHelper;
-
-    /**
-     * Default constructor.
-     *
-     * @param textView TextView instance
-     */
-    public EmojiTextViewHelper(@NonNull TextView textView) {
-        Preconditions.checkNotNull(textView, "textView cannot be null");
-        mHelper = Build.VERSION.SDK_INT >= 19 ? new HelperInternal19(textView)
-                : new HelperInternal();
-    }
-
-    /**
-     * Updates widget's TransformationMethod so that the transformed text can be processed.
-     * Should be called in the widget constructor. When used on devices running API 18 or below,
-     * this method does nothing.
-     *
-     * @see #wrapTransformationMethod(TransformationMethod)
-     */
-    public void updateTransformationMethod() {
-        mHelper.updateTransformationMethod();
-    }
-
-    /**
-     * Appends EmojiCompat InputFilters to the widget InputFilters. Should be called by {@link
-     * TextView#setFilters(InputFilter[])} to update the InputFilters. When used on devices running
-     * API 18 or below, this method returns {@code filters} that is given as a parameter.
-     *
-     * @param filters InputFilter array passed to {@link TextView#setFilters(InputFilter[])}
-     *
-     * @return same copy if the array already contains EmojiCompat InputFilter. A new array copy if
-     * not.
-     */
-    @NonNull
-    public InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
-        return mHelper.getFilters(filters);
-    }
-
-    /**
-     * Returns transformation method that can update the transformed text to display emojis. When
-     * used on devices running API 18 or below, this method returns {@code transformationMethod}
-     * that is given as a parameter.
-     *
-     * @param transformationMethod instance to be wrapped
-     */
-    @Nullable
-    public TransformationMethod wrapTransformationMethod(
-            @Nullable TransformationMethod transformationMethod) {
-        return mHelper.wrapTransformationMethod(transformationMethod);
-    }
-
-    /**
-     * Call when allCaps is set on TextView. When used on devices running API 18 or below, this
-     * method does nothing.
-     *
-     * @param allCaps allCaps parameter passed to {@link TextView#setAllCaps(boolean)}
-     */
-    public void setAllCaps(boolean allCaps) {
-        mHelper.setAllCaps(allCaps);
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static class HelperInternal {
-
-        void updateTransformationMethod() {
-            // do nothing
-        }
-
-        InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
-            return filters;
-        }
-
-        TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) {
-            return transformationMethod;
-        }
-
-        void setAllCaps(boolean allCaps) {
-            // do nothing
-        }
-    }
-
-    @RequiresApi(19)
-    private static class HelperInternal19 extends HelperInternal {
-        private final TextView mTextView;
-        private final EmojiInputFilter mEmojiInputFilter;
-
-        HelperInternal19(TextView textView) {
-            mTextView = textView;
-            mEmojiInputFilter = new EmojiInputFilter(textView);
-        }
-
-        @Override
-        void updateTransformationMethod() {
-            final TransformationMethod tm = mTextView.getTransformationMethod();
-            if (tm != null && !(tm instanceof PasswordTransformationMethod)) {
-                mTextView.setTransformationMethod(wrapTransformationMethod(tm));
-            }
-        }
-
-        @Override
-        InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
-            final int count = filters.length;
-            for (int i = 0; i < count; i++) {
-                if (filters[i] instanceof EmojiInputFilter) {
-                    return filters;
-                }
-            }
-            final InputFilter[] newFilters = new InputFilter[filters.length + 1];
-            System.arraycopy(filters, 0, newFilters, 0, count);
-            newFilters[count] = mEmojiInputFilter;
-            return newFilters;
-        }
-
-        @Override
-        TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) {
-            if (transformationMethod instanceof EmojiTransformationMethod) {
-                return transformationMethod;
-            }
-            return new EmojiTransformationMethod(transformationMethod);
-        }
-
-        @Override
-        void setAllCaps(boolean allCaps) {
-            // When allCaps is set to false TextView sets the transformation method to be null. We
-            // are only interested when allCaps is set to true in order to wrap the original method.
-            if (allCaps) {
-                updateTransformationMethod();
-            }
-        }
-
-    }
-}
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiEditTextHelper.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiEditTextHelper.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiEditTextHelper.java
rename to emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiEditTextHelper.java
index 54216ea..90c9f82 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiEditTextHelper.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiEditTextHelper.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import android.os.Build;
 import android.text.method.KeyListener;
@@ -122,6 +122,7 @@
      *
      * @return a new KeyListener instance that wraps {@code keyListener}.
      */
+    @SuppressWarnings("ExecutorRegistration")
     @NonNull
     public KeyListener getKeyListener(@NonNull final KeyListener keyListener) {
         Preconditions.checkNotNull(keyListener, "keyListener cannot be null");
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiEditableFactory.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiEditableFactory.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiEditableFactory.java
rename to emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiEditableFactory.java
index bb6a713f..7e9c5a4 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiEditableFactory.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiEditableFactory.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import android.annotation.SuppressLint;
 import android.text.Editable;
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiInputConnection.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiInputConnection.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiInputConnection.java
rename to emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiInputConnection.java
index 03f41ce..6513aba 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiInputConnection.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiInputConnection.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import android.text.Editable;
 import android.view.inputmethod.EditorInfo;
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiInputFilter.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiInputFilter.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiInputFilter.java
rename to emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiInputFilter.java
index 6560a80..f3746de 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiInputFilter.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiInputFilter.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import android.text.Selection;
 import android.text.Spannable;
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiKeyListener.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiKeyListener.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiKeyListener.java
rename to emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiKeyListener.java
index e3d0a72..67ddc72 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiKeyListener.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiKeyListener.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import android.text.Editable;
 import android.text.method.KeyListener;
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTextViewHelper.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTextViewHelper.java
new file mode 100644
index 0000000..b4b9197
--- /dev/null
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTextViewHelper.java
@@ -0,0 +1,471 @@
+/*
+ * 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.emoji2.viewshelper;
+
+import android.os.Build;
+import android.text.InputFilter;
+import android.text.method.PasswordTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.util.SparseArray;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.util.Preconditions;
+import androidx.emoji2.text.EmojiCompat;
+
+/**
+ * Utility class to enhance custom TextView widgets with {@link EmojiCompat}.
+ * <pre>
+ * public class MyEmojiTextView extends TextView {
+ *     public MyEmojiTextView(Context context) {
+ *         super(context);
+ *         init();
+ *     }
+ *     // ..
+ *     private void init() {
+ *         getEmojiTextViewHelper().updateTransformationMethod();
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void setFilters(InputFilter[] filters) {
+ *         super.setFilters(getEmojiTextViewHelper().getFilters(filters));
+ *     }
+ *
+ *     {@literal @}Override
+ *     public void setAllCaps(boolean allCaps) {
+ *         super.setAllCaps(allCaps);
+ *         getEmojiTextViewHelper().setAllCaps(allCaps);
+ *     }
+ *
+ *     private EmojiTextViewHelper getEmojiTextViewHelper() {
+ *         if (mEmojiTextViewHelper == null) {
+ *             mEmojiTextViewHelper = new EmojiTextViewHelper(this);
+ *         }
+ *         return mEmojiTextViewHelper;
+ *     }
+ * }
+ * </pre>
+ */
+public final class EmojiTextViewHelper {
+
+    private final HelperInternal mHelper;
+
+    /**
+     * Default constructor.
+     *
+     * @param textView TextView instance
+     */
+    public EmojiTextViewHelper(@NonNull TextView textView) {
+        this(textView, true);
+    }
+
+    /**
+     * Allows skipping of all processing until EmojiCompat.init is called.
+     *
+     * @param textView TextView instance
+     * @param expectInitializedEmojiCompat if true, this helper will assume init has been called
+     *                                     and throw if it has not. If false, the methods on this
+     *                                     helper will have no effect until EmojiCompat.init is
+     *                                     called.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public EmojiTextViewHelper(@NonNull TextView textView, boolean expectInitializedEmojiCompat) {
+        Preconditions.checkNotNull(textView, "textView cannot be null");
+        if (Build.VERSION.SDK_INT < 19) {
+            mHelper = new HelperInternal();
+        } else if (!expectInitializedEmojiCompat) {
+            mHelper = new SkippingHelper19(textView);
+        } else {
+            mHelper = new HelperInternal19(textView);
+        }
+    }
+
+    /**
+     * Updates widget's TransformationMethod so that the transformed text can be processed.
+     * Should be called in the widget constructor. When used on devices running API 18 or below,
+     * this method does nothing.
+     *
+     * @see #wrapTransformationMethod(TransformationMethod)
+     */
+    public void updateTransformationMethod() {
+        mHelper.updateTransformationMethod();
+    }
+
+    /**
+     * Appends EmojiCompat InputFilters to the widget InputFilters. Should be called by {@link
+     * TextView#setFilters(InputFilter[])} to update the InputFilters. When used on devices running
+     * API 18 or below, this method returns {@code filters} that is given as a parameter.
+     *
+     * @param filters InputFilter array passed to {@link TextView#setFilters(InputFilter[])}
+     *
+     * @return same copy if the array already contains EmojiCompat InputFilter. A new array copy if
+     * not.
+     */
+    @SuppressWarnings("ArrayReturn")
+    @NonNull
+    public InputFilter[] getFilters(
+            @SuppressWarnings("ArrayReturn") @NonNull final InputFilter[] filters) {
+        return mHelper.getFilters(filters);
+    }
+
+    /**
+     * Returns transformation method that can update the transformed text to display emojis. When
+     * used on devices running API 18 or below, this method returns {@code transformationMethod}
+     * that is given as a parameter.
+     *
+     * @param transformationMethod instance to be wrapped
+     */
+    @Nullable
+    public TransformationMethod wrapTransformationMethod(
+            @Nullable TransformationMethod transformationMethod) {
+        return mHelper.wrapTransformationMethod(transformationMethod);
+    }
+
+    /**
+     * When enabled, methods will have their documented behavior.
+     *
+     * When disabled, all methods will have no effect and emoji will not be processed.
+     *
+     * Setting this to disable will also have the side effect of setting both the transformation
+     * method and filter if enabled has changed since the last call. By default
+     * EmojiTextViewHelper is enabled.
+     *
+     * You do not need to call {@link EmojiTextViewHelper#updateTransformationMethod()} again after
+     * calling setEnabled.
+     *
+     * @param enabled if this helper should process emoji.
+     */
+    public void setEnabled(boolean enabled) {
+        mHelper.setEnabled(enabled);
+    }
+
+    /**
+     * Call when allCaps is set on TextView. When used on devices running API 18 or below, this
+     * method does nothing.
+     *
+     * @param allCaps allCaps parameter passed to {@link TextView#setAllCaps(boolean)}
+     */
+    public void setAllCaps(boolean allCaps) {
+        mHelper.setAllCaps(allCaps);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    static class HelperInternal {
+
+        void updateTransformationMethod() {
+            // do nothing
+        }
+
+        @NonNull
+        InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
+            return filters;
+        }
+
+        @Nullable
+        TransformationMethod wrapTransformationMethod(
+                @Nullable TransformationMethod transformationMethod) {
+            return transformationMethod;
+        }
+
+        void setAllCaps(boolean allCaps) {
+            // do nothing
+        }
+
+        void setEnabled(boolean processEmoji) {
+            // do nothing
+        }
+    }
+
+    /**
+     * This helper allows EmojiTextViewHelper to skip all calls to EmojiCompat until
+     * {@link EmojiCompat#isConfigured()} returns true on devices that are 19+.
+     *
+     * When isConfigured returns true, this delegates to {@link HelperInternal19} to provide
+     * EmojiCompat behavior. This has the effect of making EmojiCompat calls a "no-op" when
+     * EmojiCompat is not configured on a device.
+     *
+     * There is no mechanism to be informed when isConfigured becomes true as it will lead to
+     * likely memory leaks in situations where isConfigured never becomes true, and it is the
+     * responsibility of the caller to call
+     * {@link EmojiTextViewHelper#updateTransformationMethod()} after configuring EmojiCompat if
+     * TextView's using EmojiTextViewHelper are already displayed to the user.
+     */
+    @RequiresApi(19)
+    private static class SkippingHelper19 extends HelperInternal {
+        private HelperInternal19 mHelperDelegate;
+
+        SkippingHelper19(TextView textView) {
+            mHelperDelegate = new HelperInternal19(textView);
+        }
+
+
+        private boolean skipBecauseEmojiCompatNotInitialized() {
+            return !EmojiCompat.isConfigured();
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This method will have no effect if !{@link EmojiCompat#isConfigured()}
+         */
+        @Override
+        void updateTransformationMethod() {
+            if (skipBecauseEmojiCompatNotInitialized()) {
+                return;
+            }
+            mHelperDelegate.updateTransformationMethod();
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This method will have no effect if !{@link EmojiCompat#isConfigured()}
+         */
+        @NonNull
+        @Override
+        InputFilter[] getFilters(@NonNull InputFilter[] filters) {
+            if (skipBecauseEmojiCompatNotInitialized()) {
+                return filters;
+            }
+            return mHelperDelegate.getFilters(filters);
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This method will have no effect if !{@link EmojiCompat#isConfigured()}
+         */
+        @Nullable
+        @Override
+        TransformationMethod wrapTransformationMethod(
+                @Nullable TransformationMethod transformationMethod) {
+            if (skipBecauseEmojiCompatNotInitialized()) {
+                return transformationMethod;
+            }
+            return mHelperDelegate.wrapTransformationMethod(transformationMethod);
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This method will have no effect if !{@link EmojiCompat#isConfigured()}
+         */
+        @Override
+        void setAllCaps(boolean allCaps) {
+            if (skipBecauseEmojiCompatNotInitialized()) {
+                return;
+            }
+            mHelperDelegate.setAllCaps(allCaps);
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * This method will track enabled, but have no other effect if
+         * !{@link EmojiCompat#isConfigured()}
+         */
+        @Override
+        void setEnabled(boolean processEmoji) {
+            if (skipBecauseEmojiCompatNotInitialized()) {
+                mHelperDelegate.setEnabledUnsafe(processEmoji);
+            } else {
+                mHelperDelegate.setEnabled(processEmoji);
+            }
+        }
+    }
+
+    @RequiresApi(19)
+    private static class HelperInternal19 extends HelperInternal {
+        private final TextView mTextView;
+        private final EmojiInputFilter mEmojiInputFilter;
+        private boolean mEnabled;
+
+        HelperInternal19(TextView textView) {
+            mTextView = textView;
+            mEnabled = true;
+            mEmojiInputFilter = new EmojiInputFilter(textView);
+        }
+
+
+        @Override
+        void updateTransformationMethod() {
+            // since this is not a pure function, we need to have a side effect for both enabled
+            // and disabled
+            final TransformationMethod tm =
+                    wrapTransformationMethod(mTextView.getTransformationMethod());
+            mTextView.setTransformationMethod(tm);
+        }
+
+        /**
+         * Call whenever mEnabled changes
+         */
+        private void updateFilters() {
+            InputFilter[] oldFilters = mTextView.getFilters();
+            mTextView.setFilters(getFilters(oldFilters));
+        }
+
+        @NonNull
+        @Override
+        InputFilter[] getFilters(@NonNull final InputFilter[] filters) {
+            if (!mEnabled) {
+                // remove any EmojiInputFilter when disabled
+                return removeEmojiInputFilterIfPresent(filters);
+            } else {
+                return addEmojiInputFilterIfMissing(filters);
+            }
+        }
+
+        /**
+         * Make sure that EmojiInputFilter is present in filters, or add it.
+         *
+         * @param filters to check
+         * @return filters with mEmojiInputFilter added, if not previously present
+         */
+        @NonNull
+        private InputFilter[] addEmojiInputFilterIfMissing(@NonNull InputFilter[] filters) {
+            final int count = filters.length;
+            for (int i = 0; i < count; i++) {
+                if (filters[i] == mEmojiInputFilter) {
+                    return filters;
+                }
+            }
+            final InputFilter[] newFilters = new InputFilter[filters.length + 1];
+            System.arraycopy(filters, 0, newFilters, 0, count);
+            newFilters[count] = mEmojiInputFilter;
+            return newFilters;
+        }
+
+        /**
+         * Remove all EmojiInputFilter from filters
+         *
+         * @return filters.filter { it !== mEmojiInputFilter }
+         */
+        @NonNull
+        private InputFilter[] removeEmojiInputFilterIfPresent(@NonNull InputFilter[] filters) {
+            // find out the new size after removing (all) EmojiInputFilter
+            SparseArray<InputFilter> filterSet = getEmojiInputFilterPositionArray(filters);
+            if (filterSet.size() == 0) {
+                return filters;
+            }
+
+
+            final int inCount = filters.length;
+            int outCount = filters.length - filterSet.size();
+            InputFilter[] result = new InputFilter[outCount];
+            int destPosition = 0;
+            for (int srcPosition = 0; srcPosition < inCount; srcPosition++) {
+                if (filterSet.indexOfKey(srcPosition) < 0) {
+                    result[destPosition] = filters[srcPosition];
+                    destPosition++;
+                }
+            }
+            return result;
+        }
+
+        /**
+         * Populate a sparse array with true for all indexes that contain an EmojiInputFilter.
+         */
+        private SparseArray<InputFilter> getEmojiInputFilterPositionArray(
+                @NonNull InputFilter[] filters) {
+            SparseArray<InputFilter> result = new SparseArray<>(1);
+            for (int pos = 0; pos < filters.length; pos++) {
+                if (filters[pos] instanceof EmojiInputFilter) {
+                    result.put(pos, filters[pos]);
+                }
+            }
+            return result;
+        }
+
+        @Nullable
+        @Override
+        TransformationMethod wrapTransformationMethod(
+                @Nullable TransformationMethod transformationMethod) {
+            if (mEnabled) {
+                return wrapForEnabled(transformationMethod);
+            } else {
+                return unwrapForDisabled(transformationMethod);
+            }
+        }
+
+        /**
+         * Unwrap EmojiTransformationMethods safely.
+         */
+        @Nullable
+        private TransformationMethod unwrapForDisabled(
+                @Nullable TransformationMethod transformationMethod) {
+            if (transformationMethod instanceof EmojiTransformationMethod) {
+                EmojiTransformationMethod etm =
+                        (EmojiTransformationMethod) transformationMethod;
+                return etm.getOriginalTransformationMethod();
+            } else {
+                return transformationMethod;
+            }
+        }
+
+        /**
+         * Wrap in EmojiTransformationMethod, but don't double wrap.
+         *
+         * This will not wrap {@link PasswordTransformationMethod}.
+         */
+        @NonNull
+        private TransformationMethod wrapForEnabled(
+                @Nullable TransformationMethod transformationMethod) {
+            if (transformationMethod instanceof EmojiTransformationMethod) {
+                return transformationMethod;
+            } else if (transformationMethod instanceof PasswordTransformationMethod) {
+                return transformationMethod;
+            } else {
+                return new EmojiTransformationMethod(transformationMethod);
+            }
+        }
+
+        @Override
+        void setAllCaps(boolean allCaps) {
+            // When allCaps is set to false TextView sets the transformation method to be null. We
+            // are only interested when allCaps is set to true in order to wrap the original method.
+            if (allCaps) {
+                updateTransformationMethod();
+            }
+        }
+
+        @Override
+        void setEnabled(boolean enabled) {
+            boolean oldValue = mEnabled;
+            mEnabled = enabled;
+            if (oldValue != enabled) {
+                updateTransformationMethod();
+                updateFilters();
+            }
+        }
+
+        /**
+         * Call to set enabled without side effects. Should only be used when EmojiCompat is not
+         * initialized.
+         *
+         * @param processEmoji when true, this helper will process emoji
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        void setEnabledUnsafe(boolean processEmoji) {
+            mEnabled = processEmoji;
+        }
+    }
+}
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTextWatcher.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTextWatcher.java
similarity index 98%
rename from emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTextWatcher.java
rename to emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTextWatcher.java
index c86f3e2..73d5653 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTextWatcher.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTextWatcher.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import android.text.Editable;
 import android.text.Selection;
diff --git a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTransformationMethod.java b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTransformationMethod.java
similarity index 85%
rename from emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTransformationMethod.java
rename to emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTransformationMethod.java
index 47484e5..bd3561f 100644
--- a/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/helpers/EmojiTransformationMethod.java
+++ b/emoji2/emoji2-views-helper/src/main/java/androidx/emoji2/viewshelper/EmojiTransformationMethod.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.emoji2.helpers;
+package androidx.emoji2.viewshelper;
 
 import android.graphics.Rect;
 import android.text.method.TransformationMethod;
@@ -33,9 +33,10 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @RequiresApi(19)
 class EmojiTransformationMethod implements TransformationMethod {
+    @Nullable
     private final TransformationMethod mTransformationMethod;
 
-    EmojiTransformationMethod(TransformationMethod transformationMethod) {
+    EmojiTransformationMethod(@Nullable TransformationMethod transformationMethod) {
         mTransformationMethod = transformationMethod;
     }
 
@@ -71,4 +72,12 @@
                     previouslyFocusedRect);
         }
     }
+
+    /**
+     * Get the original transformation method that this is wrapping
+     * @return any transformation methods this emoji transformation method was wrapping
+     */
+    public TransformationMethod getOriginalTransformationMethod() {
+        return mTransformationMethod;
+    }
 }
diff --git a/emoji2/emoji2-views/api/current.txt b/emoji2/emoji2-views/api/current.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/current.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/public_plus_experimental_current.txt b/emoji2/emoji2-views/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..b77930a
--- /dev/null
+++ b/emoji2/emoji2-views/api/public_plus_experimental_current.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(@androidx.emoji2.text.EmojiCompat.ReplaceStrategy int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/res-current.txt b/emoji2/emoji2-views/api/res-current.txt
new file mode 100644
index 0000000..8bc8423
--- /dev/null
+++ b/emoji2/emoji2-views/api/res-current.txt
@@ -0,0 +1,2 @@
+attr emojiReplaceStrategy
+attr maxEmojiCount
diff --git a/emoji2/emoji2-views/api/restricted_current.txt b/emoji2/emoji2-views/api/restricted_current.txt
new file mode 100644
index 0000000..b77930a
--- /dev/null
+++ b/emoji2/emoji2-views/api/restricted_current.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(@androidx.emoji2.text.EmojiCompat.ReplaceStrategy int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/build.gradle b/emoji2/emoji2-views/build.gradle
index 5a66a08..a7b3177 100644
--- a/emoji2/emoji2-views/build.gradle
+++ b/emoji2/emoji2-views/build.gradle
@@ -21,9 +21,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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 project(':internal-testutils-runtime')
 }
 
@@ -37,7 +37,7 @@
 
 androidx {
     name = "Android Emoji2 Compat Views"
-    publish = Publish.NONE
+    publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.EMOJI2
     mavenGroup = LibraryGroups.EMOJI2
     inceptionYear = "2017"
diff --git a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiButton.java b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiButton.java
index 08d5465..373e03e 100644
--- a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiButton.java
+++ b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiButton.java
@@ -24,7 +24,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.widget.TextViewCompat;
-import androidx.emoji2.helpers.EmojiTextViewHelper;
+import androidx.emoji2.viewshelper.EmojiTextViewHelper;
 
 /**
  * Button widget enhanced with emoji capability by using {@link EmojiTextViewHelper}. When used
@@ -62,7 +62,7 @@
     }
 
     @Override
-    public void setFilters(@NonNull InputFilter[] filters) {
+    public void setFilters(@SuppressWarnings("ArrayReturn") @NonNull InputFilter[] filters) {
         super.setFilters(getEmojiTextViewHelper().getFilters(filters));
     }
 
diff --git a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiEditText.java b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiEditText.java
index b0925a6..b7dbdc3 100644
--- a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiEditText.java
+++ b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiEditText.java
@@ -27,8 +27,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.widget.TextViewCompat;
-import androidx.emoji2.helpers.EmojiEditTextHelper;
 import androidx.emoji2.text.EmojiCompat;
+import androidx.emoji2.viewshelper.EmojiEditTextHelper;
 
 /**
  * EditText widget enhanced with emoji capability by using {@link EmojiEditTextHelper}. When used
diff --git a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiExtractEditText.java b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiExtractEditText.java
index fb7443c..6f625f8 100644
--- a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiExtractEditText.java
+++ b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiExtractEditText.java
@@ -30,9 +30,9 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.core.widget.TextViewCompat;
-import androidx.emoji2.helpers.EmojiEditTextHelper;
 import androidx.emoji2.text.EmojiCompat;
 import androidx.emoji2.text.EmojiSpan;
+import androidx.emoji2.viewshelper.EmojiEditTextHelper;
 
 /**
  * ExtractEditText widget enhanced with emoji capability by using {@link EmojiEditTextHelper}.
diff --git a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiTextView.java b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiTextView.java
index 137deb2..d32dcf3 100644
--- a/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiTextView.java
+++ b/emoji2/emoji2-views/src/main/java/androidx/emoji2/widget/EmojiTextView.java
@@ -24,7 +24,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.widget.TextViewCompat;
-import androidx.emoji2.helpers.EmojiTextViewHelper;
+import androidx.emoji2.viewshelper.EmojiTextViewHelper;
 
 /**
  * TextView widget enhanced with emoji capability by using {@link EmojiTextViewHelper}. When used
@@ -62,7 +62,7 @@
     }
 
     @Override
-    public void setFilters(@NonNull InputFilter[] filters) {
+    public void setFilters(@SuppressWarnings("ArrayReturn") @NonNull InputFilter[] filters) {
         super.setFilters(getEmojiTextViewHelper().getFilters(filters));
     }
 
diff --git a/emoji2/emoji2/api/current.txt b/emoji2/emoji2/api/current.txt
new file mode 100644
index 0000000..f69bb70
--- /dev/null
+++ b/emoji2/emoji2/api/current.txt
@@ -0,0 +1,106 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.EmojiCompat.Config? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method public boolean hasEmojiGlyph(CharSequence);
+    method public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+}
+
diff --git a/emoji2/emoji2/api/public_plus_experimental_current.txt b/emoji2/emoji2/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..f69bb70
--- /dev/null
+++ b/emoji2/emoji2/api/public_plus_experimental_current.txt
@@ -0,0 +1,106 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.EmojiCompat.Config? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method public boolean hasEmojiGlyph(CharSequence);
+    method public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+}
+
diff --git a/emoji2/emoji2/api/res-current.txt b/emoji2/emoji2/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2/api/res-current.txt
diff --git a/emoji2/emoji2/api/restricted_current.txt b/emoji2/emoji2/api/restricted_current.txt
new file mode 100644
index 0000000..f69bb70
--- /dev/null
+++ b/emoji2/emoji2/api/restricted_current.txt
@@ -0,0 +1,106 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.EmojiCompat.Config? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method public boolean hasEmojiGlyph(CharSequence);
+    method public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+}
+
diff --git a/emoji2/emoji2/build.gradle b/emoji2/emoji2/build.gradle
index fd53536..ef1872e 100644
--- a/emoji2/emoji2/build.gradle
+++ b/emoji2/emoji2/build.gradle
@@ -13,8 +13,12 @@
 
 BundleInsideHelper.forInsideAar(
     project,
-    /* from = */ "com.google.flatbuffers",
-    /* to =   */ "androidx.text.emoji.flatbuffer"
+    [
+            new BundleInsideHelper.Relocation("com.google.flatbuffers",
+                    "androidx.emoji2.text.flatbuffer"),
+            new BundleInsideHelper.Relocation("androidx.text.emoji.flatbuffer",
+                    "androidx.emoji2.text.flatbuffer")
+    ]
 )
 
 dependencies {
@@ -28,15 +32,15 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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 project(':internal-testutils-runtime')
 }
 
 androidx {
     name = "Android Emoji2 Compat"
-    publish = Publish.NONE
+    publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.EMOJI2
     mavenGroup = LibraryGroups.EMOJI2
     inceptionYear = "2017"
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
index 94ecef2..07b735d 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
@@ -379,6 +379,41 @@
     }
 
     /**
+     * Return true if EmojiCompat has been configured by a successful call to
+     * {@link EmojiCompat#init}.
+     *
+     * You can use this to check if {@link EmojiCompat#get()} will return a valid EmojiCompat
+     * instance.
+     *
+     * This function does not check the {@link #getLoadState()} and will return true even if the
+     * font is still loading, or has failed to load.
+     *
+     * @return true if EmojiCompat has been successfully initialized.
+     */
+    @SuppressWarnings("GuardedBy") // same rationale as double-check lock
+    public static boolean isConfigured() {
+        // Note: this is true immediately after calling .init(Config).
+        //
+        // These are three situations this may return false
+        //   1) An app has disabled EmojiCompatInitializer and does not intend to call .init.
+        //   2) EmojiCompatInitializer did not find a configuration
+        //   3) EmojiCompatInitializer was disable or failed, and the app will call .init. In the
+        //   future it will return true.
+        //
+        // In case one and two, this method will always return false for the duration of the
+        // application lifecycle.
+        //
+        // In case three, this will return true at some future point. There is no callback
+        // mechanism to learn about the init call due to the high potential for leaked references
+        // in a static context if it's actually case 2 (when using manual callback registration).
+        //
+        // It is recommended that applications call init prior to creating any screens that
+        // may show emoji or user generated content.
+        return sInstance != null;
+    }
+
+
+    /**
      * Used by the tests to reset EmojiCompat with a new configuration. Every time it is called a
      * new instance is created with the new configuration.
      *
@@ -527,6 +562,7 @@
      *
      * @see #unregisterInitCallback(InitCallback)
      */
+    @SuppressWarnings("ExecutorRegistration")
     public void registerInitCallback(@NonNull InitCallback initCallback) {
         Preconditions.checkNotNull(initCallback, "initCallback cannot be null");
 
@@ -946,6 +982,7 @@
          *
          * @param loaderCallback callback to signal the loading state
          */
+        @SuppressWarnings("ExecutorRegistration")
         void load(@NonNull MetadataRepoLoaderCallback loaderCallback);
     }
 
@@ -1067,6 +1104,7 @@
          *
          * @return EmojiCompat.Config instance
          */
+        @SuppressWarnings("ExecutorRegistration")
         @NonNull
         public Config registerInitCallback(@NonNull InitCallback initCallback) {
             Preconditions.checkNotNull(initCallback, "initCallback cannot be null");
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompatInitializer.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompatInitializer.java
index 2be308f..462d1d4 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompatInitializer.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompatInitializer.java
@@ -49,8 +49,6 @@
  *     </provider>
  * </pre>
  *
- * For more information see {@link https://developer.android.com/topic/libraries/app-startup#manual}
- *
  * @see androidx.emoji2.text.DefaultEmojiCompatConfig
  */
 public class EmojiCompatInitializer implements Initializer<Boolean> {
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiMetadata.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiMetadata.java
index 8f15f52..7244296 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiMetadata.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiMetadata.java
@@ -28,8 +28,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
-import androidx.text.emoji.flatbuffer.MetadataItem;
-import androidx.text.emoji.flatbuffer.MetadataList;
+import androidx.emoji2.text.flatbuffer.MetadataItem;
+import androidx.emoji2.text.flatbuffer.MetadataList;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
index 28f39ef..145af0d 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
@@ -79,7 +79,8 @@
 
     @Override
     public int getSize(@NonNull final Paint paint,
-            @SuppressLint("UnknownNullness") final CharSequence text,
+            @SuppressLint("UnknownNullness") @SuppressWarnings("MissingNullability")
+            final CharSequence text,
             final int start,
             final int end,
             @Nullable final Paint.FontMetricsInt fm) {
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataListReader.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataListReader.java
index 674202d..2ed42e0 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataListReader.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataListReader.java
@@ -22,7 +22,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
-import androidx.text.emoji.flatbuffer.MetadataList;
+import androidx.emoji2.text.flatbuffer.MetadataList;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
index 27afc5d..bc31e26 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
@@ -25,7 +25,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.util.Preconditions;
-import androidx.text.emoji.flatbuffer.MetadataList;
+import androidx.emoji2.text.flatbuffer.MetadataList;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/fragment/fragment-testing/build.gradle b/fragment/fragment-testing/build.gradle
index 716c481..6c9828d 100644
--- a/fragment/fragment-testing/build.gradle
+++ b/fragment/fragment-testing/build.gradle
@@ -36,8 +36,8 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
 
     lintPublish(project(":fragment:fragment-testing-lint"))
 }
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index 57dd500..da73487 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -58,9 +58,9 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-runtime"), {
         exclude group: "androidx.fragment", module: "fragment"
diff --git a/gradlew b/gradlew
index a43ff0f..4067d06 100755
--- a/gradlew
+++ b/gradlew
@@ -222,8 +222,14 @@
   TMPDIR_ARG="-Djava.io.tmpdir=$TMPDIR"
 fi
 
+if [[ " ${@} " =~ " --clean " ]]; then
+  cleanCaches=true
+else
+  cleanCaches=false
+fi
+
 # Expand some arguments
-for compact in "--ci" "--strict"; do
+for compact in "--ci" "--strict" "--clean"; do
   if [ "$compact" == "--ci" ]; then
     expanded="--strict\
      --stacktrace\
@@ -240,30 +246,38 @@
      --no-daemon\
      --offline"
   fi
+  if [ "$compact" == "--clean" ]; then
+    expanded="" # we parsed the argument above but we still have to remove it to avoid confusing Gradle
+  fi
 
-  # Expand an individual argument
-  # Start by making a copy of our list of arguments and iterating through the copy
-  for arg in "$@"; do
-    # Remove this argument from our list of arguments.
-    # By the time we've completed this loop, we will have removed the original copy of
-    # each argument, and potentially re-added a new copy or an expansion of each.
-    shift
-    # Determine whether to expand this argument
-    if [ "$arg" == "$compact" ]; then
-      # Add the expansion to our arguments
-      set -- "$@" $expanded
-      echo "gradlew expanded '$compact' into '$expanded'"
-      echo
-      # We avoid re-adding this argument itself back into the list for two reasons:
-      # 1. This argument might not be directly understood by Gradle
-      # 2. We want to enforce that all behaviors enabled by this flag can be toggled independently,
-      # so we don't want it to be easy to inadvertently check for the presence of this flag
-      # specifically
-    else
-      # Add this argument back into our arguments
-      set -- "$@" "$arg"
-    fi
-  done
+  # check whether this particular compat argument was passed (and therefore needs expansion)
+  if [[ " ${@} " =~ " $compact " ]]; then
+    # Expand an individual argument
+    # Start by making a copy of our list of arguments and iterating through the copy
+    for arg in "$@"; do
+      # Remove this argument from our list of arguments.
+      # By the time we've completed this loop, we will have removed the original copy of
+      # each argument, and potentially re-added a new copy or an expansion of each.
+      shift
+      # Determine whether to expand this argument
+      if [ "$arg" == "$compact" ]; then
+        # Add the expansion to our arguments
+        set -- "$@" $expanded
+        if [ "$expanded" != "" ]; then
+          echo "gradlew expanded '$compact' into '$expanded'"
+          echo
+        fi
+        # We avoid re-adding this argument itself back into the list for two reasons:
+        # 1. This argument might not be directly understood by Gradle
+        # 2. We want to enforce that all behaviors enabled by this flag can be toggled independently,
+        # so we don't want it to be easy to inadvertently check for the presence of this flag
+        # specifically
+      else
+        # Add this argument back into our arguments
+        set -- "$@" "$arg"
+      fi
+    done
+  fi
 done
 
 function tryToDiagnosePossibleDaemonFailure() {
@@ -276,6 +290,41 @@
   fi
 }
 
+function removeCaches() {
+  rm -rf $SCRIPT_PATH/.gradle
+  rm -rf $SCRIPT_PATH/buildSrc/.gradle
+  rm -f  $SCRIPT_PATH/local.properties
+  if [ "$GRADLE_USER_HOME" != "" ]; then
+    rm -rf "$GRADLE_USER_HOME"
+  else
+    rm -rf ~/.gradle
+  fi
+  # AGP should (also) do this automatically (b/170640263)
+  rm -rf $SCRIPT_PATH/appsearch/appsearch/.cxx
+  rm -rf $SCRIPT_PATH/appsearch/local-backend/.cxx
+  rm -rf $SCRIPT_PATH/appsearch/local-storage/.cxx
+  rm -rf $OUT_DIR
+}
+
+if [ "$cleanCaches" == true ]; then
+  echo "IF ./gradlew --clean FIXES YOUR BUILD; OPEN A BUG."
+  echo "In nearly all cases, it should not be necessary to run a clean build."
+  echo
+  echo "You may be more interested in running:"
+  echo
+  echo "  ./development/diagnose-build-failure/diagnose-build-failure.sh $*"
+  echo
+  echo "which attempts to diagnose more details about build failures."
+  echo
+  echo "Removing caches"
+  # one case where it is convenient to have a clean build is for double-checking that a build failure isn't due to an incremental build failure
+  # another case where it is convenient to have a clean build is for performance testing
+  # another case where it is convenient to have a clean build is when you're modifying the build and may have introduced some errors but haven't shared your changes yet (at which point you should have fixed the errors)
+  echo
+
+  removeCaches
+fi
+
 function runGradle() {
   processOutput=false
   if [[ " ${@} " =~ " -Pandroidx.validateNoUnrecognizedMessages " ]]; then
diff --git a/gridlayout/gridlayout/build.gradle b/gridlayout/gridlayout/build.gradle
index 613a911..b5c5616 100644
--- a/gridlayout/gridlayout/build.gradle
+++ b/gridlayout/gridlayout/build.gradle
@@ -16,7 +16,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
 }
 
 androidx {
diff --git a/heifwriter/heifwriter/build.gradle b/heifwriter/heifwriter/build.gradle
index b31fef5..2df6655 100644
--- a/heifwriter/heifwriter/build.gradle
+++ b/heifwriter/heifwriter/build.gradle
@@ -21,7 +21,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
 }
 
 androidx {
diff --git a/leanback/leanback-paging/build.gradle b/leanback/leanback-paging/build.gradle
index 32e4434..415101b 100644
--- a/leanback/leanback-paging/build.gradle
+++ b/leanback/leanback-paging/build.gradle
@@ -19,9 +19,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-espresso"))
     androidTestImplementation(project(":internal-testutils-runtime"))
     androidTestImplementation(project(":internal-testutils-common"))
diff --git a/leanback/leanback-tab/build.gradle b/leanback/leanback-tab/build.gradle
index 8bad67a..587ee18 100644
--- a/leanback/leanback-tab/build.gradle
+++ b/leanback/leanback-tab/build.gradle
@@ -25,9 +25,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-espresso"))
     androidTestImplementation(project(":internal-testutils-runtime"))
     androidTestImplementation(project(":internal-testutils-common"))
diff --git a/leanback/leanback/build.gradle b/leanback/leanback/build.gradle
index e0f38fa..cbc1960 100644
--- a/leanback/leanback/build.gradle
+++ b/leanback/leanback/build.gradle
@@ -23,9 +23,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-espresso"))
     androidTestImplementation(project(":internal-testutils-runtime"))
     androidTestImplementation(project(":internal-testutils-common"))
diff --git a/loader/loader-ktx/build.gradle b/loader/loader-ktx/build.gradle
index e27fe01..12f8e80 100644
--- a/loader/loader-ktx/build.gradle
+++ b/loader/loader-ktx/build.gradle
@@ -38,9 +38,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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
 }
 
 androidx {
diff --git a/loader/loader/build.gradle b/loader/loader/build.gradle
index 3b958cf..315f072 100644
--- a/loader/loader/build.gradle
+++ b/loader/loader/build.gradle
@@ -27,9 +27,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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
 }
 
 androidx {
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 49e34a9..561b834 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -31,7 +31,7 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
     androidTestImplementation(project(":internal-testutils-runtime"))
     annotationProcessor(project(":versionedparcelable:versionedparcelable-compiler"))
 }
diff --git a/media2/media2-common/build.gradle b/media2/media2-common/build.gradle
index 45d8b15..e128665 100644
--- a/media2/media2-common/build.gradle
+++ b/media2/media2-common/build.gradle
@@ -38,7 +38,7 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
     androidTestImplementation(project(":internal-testutils-runtime"))
     annotationProcessor(project(":versionedparcelable:versionedparcelable-compiler"))
 }
diff --git a/media2/media2-player/build.gradle b/media2/media2-player/build.gradle
index 73f080a..cfd7110 100644
--- a/media2/media2-player/build.gradle
+++ b/media2/media2-player/build.gradle
@@ -39,7 +39,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
     androidTestImplementation(project(":internal-testutils-runtime"))
     annotationProcessor(project(":versionedparcelable:versionedparcelable-compiler"))
 }
diff --git a/media2/media2-session/build.gradle b/media2/media2-session/build.gradle
index 4aa0edb..47e3b2e 100644
--- a/media2/media2-session/build.gradle
+++ b/media2/media2-session/build.gradle
@@ -37,7 +37,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
     androidTestImplementation(project(":internal-testutils-runtime"))
     annotationProcessor(project(":versionedparcelable:versionedparcelable-compiler"))
 }
diff --git a/media2/media2-widget/build.gradle b/media2/media2-widget/build.gradle
index f26fb57..e637e6c 100644
--- a/media2/media2-widget/build.gradle
+++ b/media2/media2-widget/build.gradle
@@ -33,9 +33,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":internal-testutils-runtime"))
     androidTestImplementation(project(":media2:media2-player"))
 }
diff --git a/mediarouter/mediarouter/build.gradle b/mediarouter/mediarouter/build.gradle
index 993ef6e..84c37b6 100644
--- a/mediarouter/mediarouter/build.gradle
+++ b/mediarouter/mediarouter/build.gradle
@@ -37,7 +37,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
     androidTestImplementation(project(":media:version-compat-tests:lib"))
 }
 
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index e45b8cf..5ecb6d0 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -46,8 +46,8 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestImplementation(KOTLIN_STDLIB)
 }
 
diff --git a/navigation/navigation-dynamic-features-fragment/build.gradle b/navigation/navigation-dynamic-features-fragment/build.gradle
index ccf402b..369f823 100644
--- a/navigation/navigation-dynamic-features-fragment/build.gradle
+++ b/navigation/navigation-dynamic-features-fragment/build.gradle
@@ -42,9 +42,9 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
     androidTestImplementation(TRUTH)
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.fragment", module: "fragment"
diff --git a/navigation/navigation-dynamic-features-runtime/build.gradle b/navigation/navigation-dynamic-features-runtime/build.gradle
index 3853ed4..e395526a 100644
--- a/navigation/navigation-dynamic-features-runtime/build.gradle
+++ b/navigation/navigation-dynamic-features-runtime/build.gradle
@@ -43,9 +43,9 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
     androidTestImplementation(TRUTH)
 }
 
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index 8117a55..9ac58b4 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -44,8 +44,8 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(ESPRESSO_INTENTS)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestImplementation(KOTLIN_STDLIB)
 }
 
diff --git a/percentlayout/percentlayout/build.gradle b/percentlayout/percentlayout/build.gradle
index ae86a44..bc6a65e 100644
--- a/percentlayout/percentlayout/build.gradle
+++ b/percentlayout/percentlayout/build.gradle
@@ -16,7 +16,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
 }
 
 android {
diff --git a/preference/preference/build.gradle b/preference/preference/build.gradle
index 5cc86e5..b26f71e 100644
--- a/preference/preference/build.gradle
+++ b/preference/preference/build.gradle
@@ -38,9 +38,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(KOTLIN_STDLIB)
     androidTestImplementation(TRUTH)
     androidTestImplementation(MULTIDEX)
diff --git a/recyclerview/recyclerview-selection/build.gradle b/recyclerview/recyclerview-selection/build.gradle
index efd061d..d7fcd32 100644
--- a/recyclerview/recyclerview-selection/build.gradle
+++ b/recyclerview/recyclerview-selection/build.gradle
@@ -33,9 +33,9 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    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(JUNIT)
 }
 
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index a6fe293..b9dc0b6 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -19,9 +19,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(TRUTH)
     androidTestImplementation(JUNIT)
     androidTestImplementation(KOTLIN_STDLIB)
diff --git a/remotecallback/remotecallback/build.gradle b/remotecallback/remotecallback/build.gradle
index 2c46eb7..a0ad9c0 100644
--- a/remotecallback/remotecallback/build.gradle
+++ b/remotecallback/remotecallback/build.gradle
@@ -32,8 +32,8 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestAnnotationProcessor (project(":remotecallback:remotecallback-processor"))
 }
 
diff --git a/room/integration-tests/autovaluetestapp/build.gradle b/room/integration-tests/autovaluetestapp/build.gradle
index aafd894..01ee90f 100644
--- a/room/integration-tests/autovaluetestapp/build.gradle
+++ b/room/integration-tests/autovaluetestapp/build.gradle
@@ -47,8 +47,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
 
     testImplementation(JUNIT)
 }
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index edcd26b..f89faf2 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -114,8 +114,8 @@
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(project(":internal-testutils-truth"))
 
 
diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
index a20e760..6985986 100644
--- a/room/runtime/build.gradle
+++ b/room/runtime/build.gradle
@@ -58,8 +58,8 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(project(":internal-testutils-truth")) // for assertThrows
     androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
 
diff --git a/settings.gradle b/settings.gradle
index b19d4ff..0d383ef 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -63,6 +63,7 @@
                 break
             case "MEDIA":
                 filter.add(BuildType.MEDIA)
+                break
             case "WEAR":
                 filter.add(BuildType.WEAR)
                 break
diff --git a/sharetarget/sharetarget/build.gradle b/sharetarget/sharetarget/build.gradle
index 3713624..e911dca 100644
--- a/sharetarget/sharetarget/build.gradle
+++ b/sharetarget/sharetarget/build.gradle
@@ -34,8 +34,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
 }
 
 androidx {
diff --git a/slices/benchmark/build.gradle b/slices/benchmark/build.gradle
index 19510e2..7b04353 100644
--- a/slices/benchmark/build.gradle
+++ b/slices/benchmark/build.gradle
@@ -35,9 +35,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
 
 androidx {
diff --git a/slices/core/build.gradle b/slices/core/build.gradle
index a0669c4..90affdb 100644
--- a/slices/core/build.gradle
+++ b/slices/core/build.gradle
@@ -33,8 +33,8 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 
     annotationProcessor (project(":versionedparcelable:versionedparcelable-compiler"))
 }
diff --git a/slices/remotecallback/build.gradle b/slices/remotecallback/build.gradle
index 2849219..c906165 100644
--- a/slices/remotecallback/build.gradle
+++ b/slices/remotecallback/build.gradle
@@ -34,8 +34,8 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestAnnotationProcessor project(":remotecallback:remotecallback-processor")
 }
 
diff --git a/slices/test/build.gradle b/slices/test/build.gradle
index 28e5a52..351e96a 100644
--- a/slices/test/build.gradle
+++ b/slices/test/build.gradle
@@ -34,9 +34,9 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
 
 androidx {
diff --git a/slices/view/build.gradle b/slices/view/build.gradle
index f88be95..48025cc 100644
--- a/slices/view/build.gradle
+++ b/slices/view/build.gradle
@@ -37,9 +37,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
 
 androidx {
diff --git a/slidingpanelayout/slidingpanelayout/build.gradle b/slidingpanelayout/slidingpanelayout/build.gradle
index 86340b6..4ac36e3 100644
--- a/slidingpanelayout/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/slidingpanelayout/build.gradle
@@ -16,7 +16,7 @@
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(TRUTH)
     androidTestImplementation(project(':internal-testutils-runtime'))
diff --git a/sqlite/sqlite-framework/src/main/java/androidx/sqlite/db/framework/FrameworkSQLiteDatabase.java b/sqlite/sqlite-framework/src/main/java/androidx/sqlite/db/framework/FrameworkSQLiteDatabase.java
index 0cc817b..c75894e 100644
--- a/sqlite/sqlite-framework/src/main/java/androidx/sqlite/db/framework/FrameworkSQLiteDatabase.java
+++ b/sqlite/sqlite-framework/src/main/java/androidx/sqlite/db/framework/FrameworkSQLiteDatabase.java
@@ -124,7 +124,7 @@
 
     // Adding @RequiresApi(30) would prevent unbundled implementations from offering this
     // functionality to lower API levels.
-    @SuppressWarnings("UnsafeNewApiCall")
+    @SuppressWarnings("ClassVerificationFailure")
     @Override
     public void execPerConnectionSQL(@NonNull String sql, @Nullable Object[] bindArgs) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
diff --git a/startup/integration-tests/first-library/build.gradle b/startup/integration-tests/first-library/build.gradle
index 7b408f1..59ce01d 100644
--- a/startup/integration-tests/first-library/build.gradle
+++ b/startup/integration-tests/first-library/build.gradle
@@ -33,7 +33,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     testImplementation(JUNIT)
 }
diff --git a/startup/integration-tests/second-library/build.gradle b/startup/integration-tests/second-library/build.gradle
index 36ee190..760958b 100644
--- a/startup/integration-tests/second-library/build.gradle
+++ b/startup/integration-tests/second-library/build.gradle
@@ -32,7 +32,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     testImplementation(JUNIT)
 }
diff --git a/startup/startup-runtime/build.gradle b/startup/startup-runtime/build.gradle
index 75c3825..9b60091 100644
--- a/startup/startup-runtime/build.gradle
+++ b/startup/startup-runtime/build.gradle
@@ -46,8 +46,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     testImplementation(JUNIT)
 }
 
diff --git a/swiperefreshlayout/swiperefreshlayout/build.gradle b/swiperefreshlayout/swiperefreshlayout/build.gradle
index 3eb08fd..94f6bce 100644
--- a/swiperefreshlayout/swiperefreshlayout/build.gradle
+++ b/swiperefreshlayout/swiperefreshlayout/build.gradle
@@ -18,10 +18,10 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(ESPRESSO_CONTRIB, 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(project(":internal-testutils-espresso"))
     androidTestImplementation(project(":internal-testutils-runtime"), {
         exclude group: "androidx.swiperefreshlayout", module: "swiperefreshlayout"
diff --git a/testutils/testutils-espresso/build.gradle b/testutils/testutils-espresso/build.gradle
index 0ba77aa..314bef2 100644
--- a/testutils/testutils-espresso/build.gradle
+++ b/testutils/testutils-espresso/build.gradle
@@ -25,7 +25,7 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
 
-    implementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    implementation(ESPRESSO_CORE, excludes.espresso)
     implementation(KOTLIN_STDLIB)
 }
 
diff --git a/testutils/testutils-mockito/build.gradle b/testutils/testutils-mockito/build.gradle
index ea8a7de..18aaea4 100644
--- a/testutils/testutils-mockito/build.gradle
+++ b/testutils/testutils-mockito/build.gradle
@@ -23,7 +23,7 @@
 }
 
 dependencies {
-    api(MOCKITO_CORE, libs.exclude_bytebuddy)
+    api(MOCKITO_CORE, excludes.bytebuddy)
 
     implementation(KOTLIN_STDLIB)
 }
diff --git a/text/text/build.gradle b/text/text/build.gradle
index f29d53b..684036d 100644
--- a/text/text/build.gradle
+++ b/text/text/build.gradle
@@ -41,11 +41,11 @@
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(JUNIT)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(MOCKITO_KOTLIN, {
         exclude group: "org.mockito" // to keep control on the mockito version
     })
diff --git a/textclassifier/textclassifier/build.gradle b/textclassifier/textclassifier/build.gradle
index 392d82b..bc5aabc 100644
--- a/textclassifier/textclassifier/build.gradle
+++ b/textclassifier/textclassifier/build.gradle
@@ -19,9 +19,9 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
 
 android {
diff --git a/transition/transition/build.gradle b/transition/transition/build.gradle
index 7a472ef..9b95adc 100644
--- a/transition/transition/build.gradle
+++ b/transition/transition/build.gradle
@@ -23,9 +23,9 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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(project(":fragment:fragment"))
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0")
     androidTestImplementation(project(":internal-testutils-runtime"), {
diff --git a/vectordrawable/vectordrawable-animated/build.gradle b/vectordrawable/vectordrawable-animated/build.gradle
index 57a3bf7..30d3435 100644
--- a/vectordrawable/vectordrawable-animated/build.gradle
+++ b/vectordrawable/vectordrawable-animated/build.gradle
@@ -17,7 +17,7 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
 }
 
 android {
diff --git a/vectordrawable/vectordrawable-seekable/build.gradle b/vectordrawable/vectordrawable-seekable/build.gradle
index 7ac21aa..b40844f 100644
--- a/vectordrawable/vectordrawable-seekable/build.gradle
+++ b/vectordrawable/vectordrawable-seekable/build.gradle
@@ -18,7 +18,7 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(TRUTH)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
     androidTestImplementation(project(":core:core-animation-testing"))
 }
 
diff --git a/versionedparcelable/versionedparcelable/build.gradle b/versionedparcelable/versionedparcelable/build.gradle
index f4fd9d2..8a6cd7a 100644
--- a/versionedparcelable/versionedparcelable/build.gradle
+++ b/versionedparcelable/versionedparcelable/build.gradle
@@ -32,8 +32,8 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
     androidTestImplementation(TRUTH)
     androidTestAnnotationProcessor project(":versionedparcelable:versionedparcelable-compiler")
 }
diff --git a/viewpager/viewpager/build.gradle b/viewpager/viewpager/build.gradle
index 7194986..0fa3c58 100644
--- a/viewpager/viewpager/build.gradle
+++ b/viewpager/viewpager/build.gradle
@@ -24,9 +24,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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
 }
 
 androidx {
diff --git a/viewpager2/viewpager2/build.gradle b/viewpager2/viewpager2/build.gradle
index 313262f..96b5361 100644
--- a/viewpager2/viewpager2/build.gradle
+++ b/viewpager2/viewpager2/build.gradle
@@ -36,9 +36,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+    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(project(":internal-testutils-espresso"))
     androidTestImplementation(project(":internal-testutils-appcompat"), {
         exclude group: "androidx.viewpager2", module: "viewpager2"
diff --git a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/manager/TileClient.kt b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/manager/TileClient.kt
index fd0e37d..c557bdb 100644
--- a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/manager/TileClient.kt
+++ b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/manager/TileClient.kt
@@ -29,7 +29,6 @@
 import android.widget.FrameLayout
 import androidx.annotation.MainThread
 import androidx.core.content.ContextCompat
-import androidx.wear.tiles.ViewerTileUpdateRequester
 import androidx.wear.tiles.builders.LayoutElementBuilders
 import androidx.wear.tiles.builders.ResourceBuilders
 import androidx.wear.tiles.builders.TimelineBuilders
@@ -63,6 +62,11 @@
     component: ComponentName,
     private val parentView: ViewGroup
 ) : AutoCloseable {
+    private companion object {
+        private const val ACTION_REQUEST_TILE_UPDATE =
+            "androidx.wear.tiles.action.REQUEST_TILE_UPDATE"
+    }
+
     private val job = Job()
     private val coroutineScope = CoroutineScope(Dispatchers.Main + job)
 
@@ -194,7 +198,7 @@
     }
 
     private fun registerBroadcastReceiver() {
-        val i = IntentFilter(ViewerTileUpdateRequester.ACTION_REQUEST_TILE_UPDATE)
+        val i = IntentFilter(Companion.ACTION_REQUEST_TILE_UPDATE)
         context.registerReceiver(updateReceiver, i)
     }
 
diff --git a/wear/tiles/tiles/api/current.txt b/wear/tiles/tiles/api/current.txt
index 5a36e7e..ee9c959 100644
--- a/wear/tiles/tiles/api/current.txt
+++ b/wear/tiles/tiles/api/current.txt
@@ -1,12 +1,6 @@
 // Signature format: 4.0
 package androidx.wear.tiles {
 
-  public class SysUiTileUpdateRequester implements androidx.wear.tiles.TileUpdateRequester {
-    ctor public SysUiTileUpdateRequester(android.content.Context);
-    method public void requestUpdate(Class<? extends android.app.Service>);
-    field public static final String ACTION_BIND_UPDATE_REQUESTER = "androidx.wear.tiles.action.BIND_UPDATE_REQUESTER";
-  }
-
   public abstract class TileProviderService extends android.app.Service {
     ctor public TileProviderService();
     method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
@@ -23,13 +17,7 @@
   }
 
   public interface TileUpdateRequester {
-    method public void requestUpdate(Class<? extends android.app.Service>);
-  }
-
-  public class ViewerTileUpdateRequester implements androidx.wear.tiles.TileUpdateRequester {
-    ctor public ViewerTileUpdateRequester(android.content.Context);
-    method public void requestUpdate(Class<? extends android.app.Service>);
-    field public static final String ACTION_REQUEST_TILE_UPDATE = "androidx.wear.tiles.action.REQUEST_TILE_UPDATE";
+    method public void requestUpdate(Class<? extends androidx.wear.tiles.TileProviderService>);
   }
 
 }
@@ -651,6 +639,8 @@
 
   public static final class ModifiersBuilders.Padding.Builder {
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding build();
+    method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setAll(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
+    method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setAll(androidx.wear.tiles.builders.DimensionBuilders.DpProp.Builder);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.tiles.builders.DimensionBuilders.DpProp.Builder);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setEnd(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
diff --git a/wear/tiles/tiles/api/public_plus_experimental_current.txt b/wear/tiles/tiles/api/public_plus_experimental_current.txt
index 127e950..5a4d2b4 100644
--- a/wear/tiles/tiles/api/public_plus_experimental_current.txt
+++ b/wear/tiles/tiles/api/public_plus_experimental_current.txt
@@ -1,12 +1,6 @@
 // Signature format: 4.0
 package androidx.wear.tiles {
 
-  public class SysUiTileUpdateRequester implements androidx.wear.tiles.TileUpdateRequester {
-    ctor public SysUiTileUpdateRequester(android.content.Context);
-    method public void requestUpdate(Class<? extends android.app.Service>);
-    field public static final String ACTION_BIND_UPDATE_REQUESTER = "androidx.wear.tiles.action.BIND_UPDATE_REQUESTER";
-  }
-
   public abstract class TileProviderService extends android.app.Service {
     ctor public TileProviderService();
     method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
@@ -23,18 +17,12 @@
   }
 
   public interface TileUpdateRequester {
-    method public void requestUpdate(Class<? extends android.app.Service>);
+    method public void requestUpdate(Class<? extends androidx.wear.tiles.TileProviderService>);
   }
 
   @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD}) public @interface TilesExperimental {
   }
 
-  public class ViewerTileUpdateRequester implements androidx.wear.tiles.TileUpdateRequester {
-    ctor public ViewerTileUpdateRequester(android.content.Context);
-    method public void requestUpdate(Class<? extends android.app.Service>);
-    field public static final String ACTION_REQUEST_TILE_UPDATE = "androidx.wear.tiles.action.REQUEST_TILE_UPDATE";
-  }
-
 }
 
 package androidx.wear.tiles.builders {
@@ -656,6 +644,8 @@
 
   public static final class ModifiersBuilders.Padding.Builder {
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding build();
+    method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setAll(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
+    method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setAll(androidx.wear.tiles.builders.DimensionBuilders.DpProp.Builder);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.tiles.builders.DimensionBuilders.DpProp.Builder);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setEnd(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
diff --git a/wear/tiles/tiles/api/restricted_current.txt b/wear/tiles/tiles/api/restricted_current.txt
index 5a36e7e..ee9c959 100644
--- a/wear/tiles/tiles/api/restricted_current.txt
+++ b/wear/tiles/tiles/api/restricted_current.txt
@@ -1,12 +1,6 @@
 // Signature format: 4.0
 package androidx.wear.tiles {
 
-  public class SysUiTileUpdateRequester implements androidx.wear.tiles.TileUpdateRequester {
-    ctor public SysUiTileUpdateRequester(android.content.Context);
-    method public void requestUpdate(Class<? extends android.app.Service>);
-    field public static final String ACTION_BIND_UPDATE_REQUESTER = "androidx.wear.tiles.action.BIND_UPDATE_REQUESTER";
-  }
-
   public abstract class TileProviderService extends android.app.Service {
     ctor public TileProviderService();
     method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
@@ -23,13 +17,7 @@
   }
 
   public interface TileUpdateRequester {
-    method public void requestUpdate(Class<? extends android.app.Service>);
-  }
-
-  public class ViewerTileUpdateRequester implements androidx.wear.tiles.TileUpdateRequester {
-    ctor public ViewerTileUpdateRequester(android.content.Context);
-    method public void requestUpdate(Class<? extends android.app.Service>);
-    field public static final String ACTION_REQUEST_TILE_UPDATE = "androidx.wear.tiles.action.REQUEST_TILE_UPDATE";
+    method public void requestUpdate(Class<? extends androidx.wear.tiles.TileProviderService>);
   }
 
 }
@@ -651,6 +639,8 @@
 
   public static final class ModifiersBuilders.Padding.Builder {
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding build();
+    method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setAll(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
+    method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setAll(androidx.wear.tiles.builders.DimensionBuilders.DpProp.Builder);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setBottom(androidx.wear.tiles.builders.DimensionBuilders.DpProp.Builder);
     method public androidx.wear.tiles.builders.ModifiersBuilders.Padding.Builder setEnd(androidx.wear.tiles.builders.DimensionBuilders.DpProp);
diff --git a/wear/tiles/tiles/build.gradle b/wear/tiles/tiles/build.gradle
index d09b526..1856407 100644
--- a/wear/tiles/tiles/build.gradle
+++ b/wear/tiles/tiles/build.gradle
@@ -39,6 +39,7 @@
     testImplementation(ANDROIDX_TEST_CORE)
     testImplementation(ANDROIDX_TEST_RUNNER)
     testImplementation(ANDROIDX_TEST_RULES)
+    testImplementation("androidx.concurrent:concurrent-futures:1.1.0")
     testImplementation(ROBOLECTRIC)
     testImplementation(MOCKITO_CORE)
 }
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/CompositeTileUpdateRequester.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/CompositeTileUpdateRequester.java
index 402a6e2..c97773d 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/CompositeTileUpdateRequester.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/CompositeTileUpdateRequester.java
@@ -16,8 +16,6 @@
 
 package androidx.wear.tiles;
 
-import android.app.Service;
-
 import androidx.annotation.NonNull;
 
 import java.util.List;
@@ -36,7 +34,7 @@
     }
 
     @Override
-    public void requestUpdate(@NonNull Class<? extends Service> tileProvider) {
+    public void requestUpdate(@NonNull Class<? extends TileProviderService> tileProvider) {
         for (TileUpdateRequester requester : mUpdateRequesters) {
             requester.requestUpdate(tileProvider);
         }
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 6e2c156..3965e0e7 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
@@ -40,7 +40,7 @@
 
 /** Variant of {@link TileUpdateRequester} which requests an update from the Wear SysUI app. */
 // TODO(b/173688156): Renovate this whole class, and especially work around all the locks.
-public class SysUiTileUpdateRequester implements TileUpdateRequester {
+class SysUiTileUpdateRequester implements TileUpdateRequester {
     private static final String TAG = "HTUpdateRequester";
 
     private static final String DEFAULT_TARGET_SYSUI = "com.google.android.wearable.app";
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public void requestUpdate(@NonNull Class<? extends Service> tileProvider) {
+    public void requestUpdate(@NonNull Class<? extends TileProviderService> tileProvider) {
         synchronized (mLock) {
             mPendingServices.add(tileProvider);
 
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequester.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequester.java
index 4d868ae..02e3640 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequester.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequester.java
@@ -16,8 +16,6 @@
 
 package androidx.wear.tiles;
 
-import android.app.Service;
-
 import androidx.annotation.NonNull;
 
 /**
@@ -26,5 +24,5 @@
  */
 public interface TileUpdateRequester {
     /** Notify the Tile Renderer that it should fetch a new Timeline from this Tile Provider. */
-    void requestUpdate(@NonNull Class<? extends Service> tileProvider);
+    void requestUpdate(@NonNull Class<? extends TileProviderService> tileProvider);
 }
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ViewerTileUpdateRequester.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ViewerTileUpdateRequester.java
index fd33065..6ac26b7 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ViewerTileUpdateRequester.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ViewerTileUpdateRequester.java
@@ -16,7 +16,6 @@
 
 package androidx.wear.tiles;
 
-import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 
@@ -26,7 +25,7 @@
  * {@link TileUpdateRequester} which notifies the viewer that it should fetch a new version of the
  * Timeline.
  */
-public class ViewerTileUpdateRequester implements TileUpdateRequester {
+class ViewerTileUpdateRequester implements TileUpdateRequester {
     /**
      * The intent action used so a Tile Provider can request that the platform fetches a new
      * Timeline from it.
@@ -41,7 +40,7 @@
     }
 
     @Override
-    public void requestUpdate(@NonNull Class<? extends Service> tileProvider) {
+    public void requestUpdate(@NonNull Class<? extends TileProviderService> tileProvider) {
         mContext.sendBroadcast(buildUpdateIntent(mContext.getPackageName()));
     }
 
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ActionBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ActionBuilders.java
index 8d2f07b..aa76027 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ActionBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ActionBuilders.java
@@ -368,6 +368,33 @@
         @NonNull
         ActionProto.AndroidExtra toAndroidExtraProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static AndroidExtra fromAndroidExtraProto(@NonNull ActionProto.AndroidExtra proto) {
+            if (proto.hasStringVal()) {
+                return AndroidStringExtra.fromProto(proto.getStringVal());
+            }
+            if (proto.hasIntVal()) {
+                return AndroidIntExtra.fromProto(proto.getIntVal());
+            }
+            if (proto.hasLongVal()) {
+                return AndroidLongExtra.fromProto(proto.getLongVal());
+            }
+            if (proto.hasDoubleVal()) {
+                return AndroidDoubleExtra.fromProto(proto.getDoubleVal());
+            }
+            if (proto.hasBooleanVal()) {
+                return AndroidBooleanExtra.fromProto(proto.getBooleanVal());
+            }
+            throw new IllegalStateException("Proto was not a recognised instance of AndroidExtra");
+        }
+
         /** Builder to create {@link AndroidExtra} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
@@ -615,6 +642,24 @@
         @NonNull
         ActionProto.Action toActionProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static Action fromActionProto(@NonNull ActionProto.Action proto) {
+            if (proto.hasLaunchAction()) {
+                return LaunchAction.fromProto(proto.getLaunchAction());
+            }
+            if (proto.hasLoadAction()) {
+                return LoadAction.fromProto(proto.getLoadAction());
+            }
+            throw new IllegalStateException("Proto was not a recognised instance of Action");
+        }
+
         /** Builder to create {@link Action} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/DimensionBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/DimensionBuilders.java
index 2cff2b5..ac0d7fa 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/DimensionBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/DimensionBuilders.java
@@ -518,6 +518,29 @@
         @NonNull
         DimensionProto.ContainerDimension toContainerDimensionProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static ContainerDimension fromContainerDimensionProto(
+                @NonNull DimensionProto.ContainerDimension proto) {
+            if (proto.hasLinearDimension()) {
+                return DpProp.fromProto(proto.getLinearDimension());
+            }
+            if (proto.hasExpandedDimension()) {
+                return ExpandedDimensionProp.fromProto(proto.getExpandedDimension());
+            }
+            if (proto.hasWrappedDimension()) {
+                return WrappedDimensionProp.fromProto(proto.getWrappedDimension());
+            }
+            throw new IllegalStateException(
+                    "Proto was not a recognised instance of ContainerDimension");
+        }
+
         /** Builder to create {@link ContainerDimension} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
@@ -539,6 +562,29 @@
         @NonNull
         DimensionProto.ImageDimension toImageDimensionProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static ImageDimension fromImageDimensionProto(
+                @NonNull DimensionProto.ImageDimension proto) {
+            if (proto.hasLinearDimension()) {
+                return DpProp.fromProto(proto.getLinearDimension());
+            }
+            if (proto.hasExpandedDimension()) {
+                return ExpandedDimensionProp.fromProto(proto.getExpandedDimension());
+            }
+            if (proto.hasProportionalDimension()) {
+                return ProportionalDimensionProp.fromProto(proto.getProportionalDimension());
+            }
+            throw new IllegalStateException(
+                    "Proto was not a recognised instance of ImageDimension");
+        }
+
         /** Builder to create {@link ImageDimension} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
@@ -560,6 +606,23 @@
         @NonNull
         DimensionProto.SpacerDimension toSpacerDimensionProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static SpacerDimension fromSpacerDimensionProto(
+                @NonNull DimensionProto.SpacerDimension proto) {
+            if (proto.hasLinearDimension()) {
+                return DpProp.fromProto(proto.getLinearDimension());
+            }
+            throw new IllegalStateException(
+                    "Proto was not a recognised instance of SpacerDimension");
+        }
+
         /** Builder to create {@link SpacerDimension} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/LayoutElementBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/LayoutElementBuilders.java
index 060f09e..3d53863 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/LayoutElementBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/LayoutElementBuilders.java
@@ -105,10 +105,10 @@
      *
      * @hide
      */
-    @OptIn(markerClass = TilesExperimental.class)
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     @IntDef({FONT_WEIGHT_UNDEFINED, FONT_WEIGHT_NORMAL, FONT_WEIGHT_MEDIUM, FONT_WEIGHT_BOLD})
     @Retention(RetentionPolicy.SOURCE)
+    @OptIn(markerClass = TilesExperimental.class)
     public @interface FontWeight {}
 
     /** Font weight is undefined. */
@@ -1309,6 +1309,24 @@
         @NonNull
         LayoutElementProto.Span toSpanProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static Span fromSpanProto(@NonNull LayoutElementProto.Span proto) {
+            if (proto.hasText()) {
+                return SpanText.fromProto(proto.getText());
+            }
+            if (proto.hasImage()) {
+                return SpanImage.fromProto(proto.getImage());
+            }
+            throw new IllegalStateException("Proto was not a recognised instance of Span");
+        }
+
         /** Builder to create {@link Span} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
@@ -2379,6 +2397,43 @@
         @NonNull
         LayoutElementProto.LayoutElement toLayoutElementProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static LayoutElement fromLayoutElementProto(
+                @NonNull LayoutElementProto.LayoutElement proto) {
+            if (proto.hasColumn()) {
+                return Column.fromProto(proto.getColumn());
+            }
+            if (proto.hasRow()) {
+                return Row.fromProto(proto.getRow());
+            }
+            if (proto.hasBox()) {
+                return Box.fromProto(proto.getBox());
+            }
+            if (proto.hasSpacer()) {
+                return Spacer.fromProto(proto.getSpacer());
+            }
+            if (proto.hasText()) {
+                return Text.fromProto(proto.getText());
+            }
+            if (proto.hasImage()) {
+                return Image.fromProto(proto.getImage());
+            }
+            if (proto.hasArc()) {
+                return Arc.fromProto(proto.getArc());
+            }
+            if (proto.hasSpannable()) {
+                return Spannable.fromProto(proto.getSpannable());
+            }
+            throw new IllegalStateException("Proto was not a recognised instance of LayoutElement");
+        }
+
         /** Builder to create {@link LayoutElement} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
@@ -2403,6 +2458,32 @@
         @NonNull
         LayoutElementProto.ArcLayoutElement toArcLayoutElementProto();
 
+        /**
+         * Return an instance of one of this object's subtypes, from the protocol buffer
+         * representation.
+         *
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        static ArcLayoutElement fromArcLayoutElementProto(
+                @NonNull LayoutElementProto.ArcLayoutElement proto) {
+            if (proto.hasText()) {
+                return ArcText.fromProto(proto.getText());
+            }
+            if (proto.hasLine()) {
+                return ArcLine.fromProto(proto.getLine());
+            }
+            if (proto.hasSpacer()) {
+                return ArcSpacer.fromProto(proto.getSpacer());
+            }
+            if (proto.hasAdapter()) {
+                return ArcAdapter.fromProto(proto.getAdapter());
+            }
+            throw new IllegalStateException(
+                    "Proto was not a recognised instance of ArcLayoutElement");
+        }
+
         /** Builder to create {@link ArcLayoutElement} objects. */
         @SuppressLint("StaticFinalBuilder")
         interface Builder {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ModifiersBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ModifiersBuilders.java
index 25c24de..abb9b04 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ModifiersBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/builders/ModifiersBuilders.java
@@ -287,6 +287,23 @@
                 return this;
             }
 
+            /** Sets the padding for all sides of the content, in DP. */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setAll(@NonNull DpProp value) {
+                return setStart(value).setEnd(value).setTop(value).setBottom(value);
+            }
+
+            /** Sets the padding for all sides of the content, in DP. */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setAll(@NonNull DpProp.Builder valueBuilder) {
+                return setStart(valueBuilder)
+                        .setEnd(valueBuilder)
+                        .setTop(valueBuilder)
+                        .setBottom(valueBuilder);
+            }
+
             /** Builds an instance from accumulated values. */
             @NonNull
             public Padding build() {
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 3331d23..15aa6a4 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
@@ -18,12 +18,17 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.concurrent.futures.ResolvableFuture;
+import androidx.wear.tiles.builders.ResourceBuilders;
+import androidx.wear.tiles.builders.TileBuilders;
+import androidx.wear.tiles.readers.RequestReaders;
+
+import com.google.common.util.concurrent.ListenableFuture;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -60,15 +65,33 @@
     }
 
     private class FakeUpdateRequester implements TileUpdateRequester {
-        @Nullable Class<? extends Service> mCalledService = null;
+        @Nullable Class<? extends TileProviderService> mCalledService = null;
 
         @Override
-        public void requestUpdate(@NonNull Class<? extends Service> tileProvider) {
+        public void requestUpdate(@NonNull Class<? extends TileProviderService> tileProvider) {
             this.mCalledService = tileProvider;
         }
     }
 
-    private class FakeService extends Service {
+    private class FakeService extends TileProviderService {
+        @NonNull
+        @Override
+        protected ListenableFuture<TileBuilders.Tile> onTileRequest(
+                @NonNull RequestReaders.TileRequest requestParams) {
+            ResolvableFuture<TileBuilders.Tile> f = ResolvableFuture.create();
+            f.set(null);
+            return f;
+        }
+
+        @NonNull
+        @Override
+        protected ListenableFuture<ResourceBuilders.Resources> onResourcesRequest(
+                @NonNull RequestReaders.ResourcesRequest requestParams) {
+            ResolvableFuture<ResourceBuilders.Resources> f = ResolvableFuture.create();
+            f.set(null);
+            return f;
+        }
+
         @Nullable
         @Override
         public IBinder onBind(Intent intent) {
diff --git a/wear/wear-complications-data/src/test/java/androidx/wear/complications/ProviderInfoRetrieverTest.kt b/wear/wear-complications-data/src/test/java/androidx/wear/complications/ProviderInfoRetrieverTest.kt
index 6dbf82b..6f3382f 100644
--- a/wear/wear-complications-data/src/test/java/androidx/wear/complications/ProviderInfoRetrieverTest.kt
+++ b/wear/wear-complications-data/src/test/java/androidx/wear/complications/ProviderInfoRetrieverTest.kt
@@ -59,7 +59,7 @@
     private val providerInfoRetriever = ProviderInfoRetriever(mockService)
 
     @Test
-    public fun requestPreviewComplicationData() {
+    public fun retrievePreviewComplicationData() {
         runBlocking {
             val component = ComponentName("provider.package", "provider.class")
             val type = ComplicationType.LONG_TEXT
@@ -93,7 +93,7 @@
     }
 
     @Test
-    public fun requestPreviewComplicationDataProviderReturnsNull() {
+    public fun retrievePreviewComplicationDataProviderReturnsNull() {
         runBlocking {
             val component = ComponentName("provider.package", "provider.class")
             val type = ComplicationType.LONG_TEXT
@@ -116,7 +116,7 @@
     }
 
     @Test
-    public fun requestPreviewComplicationDataApiNotSupported() {
+    public fun retrievePreviewComplicationDataApiNotSupported() {
         runBlocking {
             val component = ComponentName("provider.package", "provider.class")
             val type = ComplicationType.LONG_TEXT
@@ -129,7 +129,7 @@
     }
 
     @Test
-    public fun requestPreviewComplicationDataApiReturnsFalse() {
+    public fun retrievePreviewComplicationDataApiReturnsFalse() {
         runBlocking {
             val component = ComponentName("provider.package", "provider.class")
             val type = ComplicationType.LONG_TEXT
diff --git a/wear/wear-ongoing/api/current.txt b/wear/wear-ongoing/api/current.txt
index cf3b4ea..779ba555 100644
--- a/wear/wear-ongoing/api/current.txt
+++ b/wear/wear-ongoing/api/current.txt
@@ -3,9 +3,19 @@
 
   @RequiresApi(24) public final class OngoingActivity {
     method public void apply(android.content.Context);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivityData!>);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context, int);
+    method public android.graphics.drawable.Icon? getAnimatedIcon();
+    method public String? getCategory();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public int getNotificationId();
+    method public int getOngoingActivityId();
+    method public android.graphics.drawable.Icon getStaticIcon();
+    method public androidx.wear.ongoing.OngoingActivityStatus? getStatus();
+    method public String? getTag();
+    method public long getTimestamp();
+    method public android.app.PendingIntent getTouchIntent();
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivityData!>);
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context);
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, int);
     method public void update(android.content.Context, androidx.wear.ongoing.OngoingActivityStatus);
   }
 
diff --git a/wear/wear-ongoing/api/public_plus_experimental_current.txt b/wear/wear-ongoing/api/public_plus_experimental_current.txt
index c35669e..3c959c3 100644
--- a/wear/wear-ongoing/api/public_plus_experimental_current.txt
+++ b/wear/wear-ongoing/api/public_plus_experimental_current.txt
@@ -3,9 +3,19 @@
 
   @RequiresApi(24) public final class OngoingActivity {
     method public void apply(android.content.Context);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivityData!>);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context, int);
+    method public android.graphics.drawable.Icon? getAnimatedIcon();
+    method public String? getCategory();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public int getNotificationId();
+    method public int getOngoingActivityId();
+    method public android.graphics.drawable.Icon getStaticIcon();
+    method public androidx.wear.ongoing.OngoingActivityStatus? getStatus();
+    method public String? getTag();
+    method public long getTimestamp();
+    method public android.app.PendingIntent getTouchIntent();
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivityData!>);
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context);
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, int);
     method public void update(android.content.Context, androidx.wear.ongoing.OngoingActivityStatus);
   }
 
diff --git a/wear/wear-ongoing/api/restricted_current.txt b/wear/wear-ongoing/api/restricted_current.txt
index c35669e..3c959c3 100644
--- a/wear/wear-ongoing/api/restricted_current.txt
+++ b/wear/wear-ongoing/api/restricted_current.txt
@@ -3,9 +3,19 @@
 
   @RequiresApi(24) public final class OngoingActivity {
     method public void apply(android.content.Context);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivityData!>);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context);
-    method public static androidx.wear.ongoing.OngoingActivity? fromExistingOngoingActivity(android.content.Context, int);
+    method public android.graphics.drawable.Icon? getAnimatedIcon();
+    method public String? getCategory();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public int getNotificationId();
+    method public int getOngoingActivityId();
+    method public android.graphics.drawable.Icon getStaticIcon();
+    method public androidx.wear.ongoing.OngoingActivityStatus? getStatus();
+    method public String? getTag();
+    method public long getTimestamp();
+    method public android.app.PendingIntent getTouchIntent();
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, java.util.function.Predicate<androidx.wear.ongoing.OngoingActivityData!>);
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context);
+    method public static androidx.wear.ongoing.OngoingActivity? recoverOngoingActivity(android.content.Context, int);
     method public void update(android.content.Context, androidx.wear.ongoing.OngoingActivityStatus);
   }
 
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 0879272..6c53230 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
@@ -50,11 +50,16 @@
  * notificationManager.notify(notificationId, builder.build());
  * }</pre>
  *
+ * Note that if a Notification with that id was previously posted it will be replaced. If you
+ * need more than one Notification with the same ID you can use a String tag to differentiate
+ * them in both the {@link Builder#Builder(Context, String, int, NotificationCompat.Builder)} and
+ * {@link NotificationManager#notify(String, int, Notification)}
+ * <p>
  * Afterward, {@link OngoingActivity#update(Context, OngoingActivityStatus) update} can be used to
  * update the status.
  * <p>
  * If saving the {@link OngoingActivity} instance is not convenient, it can be recovered (after the
- * notification is posted) with {@link OngoingActivity#fromExistingOngoingActivity(Context)}
+ * notification is posted) with {@link OngoingActivity#recoverOngoingActivity(Context)}
  */
 @RequiresApi(24)
 public final class OngoingActivity {
@@ -132,8 +137,6 @@
          * {@link OngoingActivity}. For example, in the WatchFace.
          * Should be white with a transparent background, preferably an AnimatedVectorDrawable.
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setAnimatedIcon(@NonNull Icon animatedIcon) {
             mAnimatedIcon = animatedIcon;
@@ -145,8 +148,6 @@
          * {@link OngoingActivity}. For example, in the WatchFace.
          * Should be white with a transparent background, preferably an AnimatedVectorDrawable.
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setAnimatedIcon(@DrawableRes int animatedIcon) {
             mAnimatedIcon = Icon.createWithResource(mContext, animatedIcon);
@@ -158,8 +159,6 @@
          * {@link OngoingActivity}, for example in the WatchFace in ambient mode.
          * Should be white with a transparent background, preferably an VectorDrawable.
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setStaticIcon(@NonNull Icon staticIcon) {
             mStaticIcon = staticIcon;
@@ -171,8 +170,6 @@
          * {@link OngoingActivity}, for example in the WatchFace in ambient mode.
          * Should be white with a transparent background, preferably an VectorDrawable.
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setStaticIcon(@DrawableRes int staticIcon) {
             mStaticIcon = Icon.createWithResource(mContext, staticIcon);
@@ -183,8 +180,6 @@
          * Set the initial status of this ongoing activity, the status may be displayed on the UI to
          * show progress of the Ongoing Activity.
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setStatus(@NonNull OngoingActivityStatus status) {
             mStatus = status;
@@ -195,8 +190,6 @@
          * Set 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)
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setTouchIntent(@NonNull PendingIntent touchIntent) {
             mTouchIntent = touchIntent;
@@ -207,8 +200,6 @@
          * Set the corresponding LocusId of this {@link OngoingActivity}, this will be used by the
          * launcher to identify the corresponding launcher item and display it accordingly.
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setLocusId(@NonNull LocusIdCompat locusId) {
             mLocusId = locusId;
@@ -217,10 +208,8 @@
 
         /**
          * Give an id to this {@link OngoingActivity}, as a way to reference it in
-         * {@link OngoingActivity#fromExistingOngoingActivity(Context, int)}
+         * {@link OngoingActivity#recoverOngoingActivity(Context, int)}
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setOngoingActivityId(int ongoingActivityId) {
             mOngoingActivityId = ongoingActivityId;
@@ -231,8 +220,6 @@
          * Set the category of this {@link OngoingActivity}, this may be used by the system to
          * prioritize it.
          */
-        @SuppressWarnings("MissingGetterMatchingBuilder")
-        // No getters needed on OngoingActivity - receiver will consume from OngoingActivityData.
         @NonNull
         public Builder setCategory(@NonNull String category) {
             mCategory = category;
@@ -291,6 +278,95 @@
     }
 
     /**
+     * Get the notificationId of the notification associated with this {@link OngoingActivity}.
+     */
+    public int getNotificationId() {
+        return mNotificationId;
+    }
+
+    /**
+     * Get the tag of the notification associated with this {@link OngoingActivity}, or null if
+     * there is none.
+     */
+    @Nullable
+    public String getTag() {
+        return mTag;
+    }
+
+    /**
+     * 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() {
+        return mData.getAnimatedIcon();
+    }
+
+    /**
+     * 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() {
+        return mData.getStaticIcon();
+    }
+
+    /**
+     * 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() {
+        return mData.getStatus();
+    }
+
+    /**
+     * 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() {
+        return mData.getTouchIntent();
+    }
+
+    /**
+     * 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() {
+        return mData.getLocusId();
+    }
+
+    /**
+     * Get the id to this {@link OngoingActivity}. This id is used to reference it in
+     * {@link #recoverOngoingActivity(Context, int)}
+     */
+    public int getOngoingActivityId() {
+        return mData.getOngoingActivityId();
+    }
+
+    /**
+     * Get the Category of this {@link OngoingActivity} if set, otherwise the category of the
+     * corresponding notification.
+     */
+    @Nullable
+    public String getCategory() {
+        return mData.getCategory();
+    }
+
+    /**
+     * Get the time (in {@link SystemClock#elapsedRealtime()} time) the OngoingActivity was built.
+     */
+    public long getTimestamp() {
+        return mData.getTimestamp();
+    }
+
+    /**
      * 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
@@ -334,7 +410,7 @@
      * @return the Ongoing Activity or null if not found
      */
     @Nullable
-    public static OngoingActivity fromExistingOngoingActivity(
+    public static OngoingActivity recoverOngoingActivity(
             @NonNull Context context,
             @NonNull Predicate<OngoingActivityData> filter
     ) {
@@ -367,8 +443,8 @@
      * @return the Ongoing Activity or null if not found
      */
     @Nullable
-    public static OngoingActivity fromExistingOngoingActivity(@NonNull Context context) {
-        return fromExistingOngoingActivity(context, (data) -> true);
+    public static OngoingActivity recoverOngoingActivity(@NonNull Context context) {
+        return recoverOngoingActivity(context, (data) -> true);
     }
 
     /**
@@ -382,9 +458,9 @@
      * @return the Ongoing Activity or null if not found
      */
     @Nullable
-    public static OngoingActivity fromExistingOngoingActivity(@NonNull Context context,
+    public static OngoingActivity recoverOngoingActivity(@NonNull Context context,
             int ongoingActivityId) {
-        return fromExistingOngoingActivity(context,
+        return recoverOngoingActivity(context,
                 (data) -> data.getOngoingActivityId() == ongoingActivityId);
     }
 
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 e8b9974..4f7f098 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
@@ -125,12 +125,12 @@
     @Test
     fun testCreateFromExistingOngoingActivityNull() {
         // Nothing to see here.
-        assertNull(OngoingActivity.fromExistingOngoingActivity(context))
+        assertNull(OngoingActivity.recoverOngoingActivity(context))
 
         // Same, with a normal notification posted.
         val builder = NotificationCompat.Builder(context, ChannelId)
         notificationManager.notify(NotificationId, builder.build())
-        assertNull(OngoingActivity.fromExistingOngoingActivity(context))
+        assertNull(OngoingActivity.recoverOngoingActivity(context))
 
         // Clean up.
         notificationManager.cancel(NotificationId)
@@ -158,7 +158,7 @@
         for (i in 1..n) {
             val status = "New Status $i"
             statuses.add(status)
-            OngoingActivity.fromExistingOngoingActivity(context, i)!!
+            OngoingActivity.recoverOngoingActivity(context, i)!!
                 .update(context, OngoingActivityStatus.forPart(TextStatusPart(status)))
         }
         assertEquals(n, statuses.size) // Just in case.
@@ -219,7 +219,7 @@
 
         // After posting, send an update.
         val newStatus = OngoingActivityStatus.forPart(TimerStatusPart(12345))
-        OngoingActivity.fromExistingOngoingActivity(context)!!.update(context, newStatus)
+        OngoingActivity.recoverOngoingActivity(context)!!.update(context, newStatus)
 
         // Get the notification and check that the status, and only the status has been updated.
         val notifications = notificationManager.activeNotifications
@@ -341,7 +341,7 @@
 
         // After posting, send an update to the second OA and check the statuses.
         val newStatus2 = OngoingActivityStatus.forPart(TextStatusPart("update2"))
-        OngoingActivity.fromExistingOngoingActivity(context, 2)?.update(context, newStatus2)
+        OngoingActivity.recoverOngoingActivity(context, 2)?.update(context, newStatus2)
 
         assertEquals("status1, update2", getStatuses())
 
diff --git a/wear/wear-watchface-client/build.gradle b/wear/wear-watchface-client/build.gradle
index 3c29c46..91ad998 100644
--- a/wear/wear-watchface-client/build.gradle
+++ b/wear/wear-watchface-client/build.gradle
@@ -42,8 +42,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(TRUTH)
 
     testImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/wear/wear-watchface-client/guava/build.gradle b/wear/wear-watchface-client/guava/build.gradle
index 3c0a1a5..a751ec3 100644
--- a/wear/wear-watchface-client/guava/build.gradle
+++ b/wear/wear-watchface-client/guava/build.gradle
@@ -37,8 +37,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(TRUTH)
 }
 
diff --git a/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationRenderer.java b/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationRenderer.java
index 1445266..82dfc2f 100644
--- a/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationRenderer.java
+++ b/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/ComplicationRenderer.java
@@ -129,9 +129,9 @@
     Drawable mLargeImage;
 
     // Drawables for rendering rounded images
-    private final RoundedDrawable mRoundedBackgroundDrawable = new RoundedDrawable();
-    private final RoundedDrawable mRoundedLargeImage = new RoundedDrawable();
-    private final RoundedDrawable mRoundedSmallImage = new RoundedDrawable();
+    private RoundedDrawable mRoundedBackgroundDrawable = null;
+    private RoundedDrawable mRoundedLargeImage = null;
+    private RoundedDrawable mRoundedSmallImage = null;
 
     // Text renderers
     private final TextRenderer mMainTextRenderer = new TextRenderer();
@@ -218,6 +218,10 @@
         }
         if (data == null) {
             mComplicationData = null;
+            // Free unnecessary RoundedDrawables.
+            mRoundedBackgroundDrawable = null;
+            mRoundedLargeImage = null;
+            mRoundedSmallImage = null;
             return;
         }
         if (data.getType() == ComplicationData.TYPE_NO_DATA) {
@@ -244,6 +248,13 @@
             loadDrawableIconAndImages();
         }
         calculateBounds();
+
+        // Based on the results of calculateBounds we know if mRoundedLargeImage or
+        // mSmallImageBounds are needed for rendering and can null the references if not required.
+        // NOTE mRoundedBackgroundDrawable has a different lifecycle which is based on the current
+        // paint mode so it doesn't make sense to clear it's reference here.
+        mRoundedLargeImage = null;
+        mRoundedSmallImage = null;
     }
 
     /**
@@ -322,10 +333,8 @@
         if (mComplicationData == null
                 || mComplicationData.getType() == ComplicationData.TYPE_EMPTY
                 || mComplicationData.getType() == ComplicationData.TYPE_NOT_CONFIGURED
-                || !mComplicationData.isActiveAt(currentTimeMillis)) {
-            return;
-        }
-        if (mBounds.isEmpty()) {
+                || !mComplicationData.isActiveAt(currentTimeMillis)
+                || mBounds.isEmpty()) {
             return;
         }
         // If in ambient mode but paint set is not usable with current ambient properties,
@@ -406,10 +415,15 @@
         canvas.drawRoundRect(mBackgroundBoundsF, radius, radius, paintSet.mBackgroundPaint);
         if (paintSet.mStyle.getBackgroundDrawable() != null
                 && !paintSet.isInBurnInProtectionMode()) {
+            if (mRoundedBackgroundDrawable == null) {
+                mRoundedBackgroundDrawable = new RoundedDrawable();
+            }
             mRoundedBackgroundDrawable.setDrawable(paintSet.mStyle.getBackgroundDrawable());
             mRoundedBackgroundDrawable.setRadius(radius);
             mRoundedBackgroundDrawable.setBounds(mBackgroundBounds);
             mRoundedBackgroundDrawable.draw(canvas);
+        } else {
+            mRoundedBackgroundDrawable = null;
         }
     }
 
@@ -520,6 +534,9 @@
         if (DEBUG_MODE) {
             canvas.drawRect(mSmallImageBounds, mDebugPaint);
         }
+        if (mRoundedSmallImage == null) {
+            mRoundedSmallImage = new RoundedDrawable();
+        }
         if (!paintSet.isInBurnInProtectionMode()) {
             mRoundedSmallImage.setDrawable(mSmallImage);
             if (mSmallImage == null) {
@@ -552,6 +569,9 @@
         }
         // Draw the image if not in burn in protection mode (in active mode or burn in not enabled)
         if (!paintSet.isInBurnInProtectionMode()) {
+            if (mRoundedLargeImage == null) {
+                mRoundedLargeImage = new RoundedDrawable();
+            }
             mRoundedLargeImage.setDrawable(mLargeImage);
             // Large image is always treated as photo style
             mRoundedLargeImage.setRadius(getImageBorderRadius(paintSet.mStyle, mLargeImageBounds));
diff --git a/wear/wear-watchface-editor/build.gradle b/wear/wear-watchface-editor/build.gradle
index 8c4d011..f226b93 100644
--- a/wear/wear-watchface-editor/build.gradle
+++ b/wear/wear-watchface-editor/build.gradle
@@ -43,8 +43,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(KOTLIN_COROUTINES_TEST)
     androidTestImplementation(TRUTH)
 }
diff --git a/wear/wear-watchface-editor/guava/build.gradle b/wear/wear-watchface-editor/guava/build.gradle
index 5d7afd9..a11ce0d 100644
--- a/wear/wear-watchface-editor/guava/build.gradle
+++ b/wear/wear-watchface-editor/guava/build.gradle
@@ -36,8 +36,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(TRUTH)
 }
 
diff --git a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
index 8654b4d..80387b8 100644
--- a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
+++ b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
@@ -142,26 +142,39 @@
  * Describes the list of [UserStyleSetting]s the user can configure.
  *
  * @param userStyleSettings The user configurable style categories associated with this watch face.
- * Empty if the watch face doesn't support user styling.
+ * Empty if the watch face doesn't support user styling. Note we allow at most one
+ * [UserStyleSetting.ComplicationsUserStyleSetting] and one
+ * [UserStyleSetting.CustomValueUserStyleSetting]
+ * in the list.
  */
 public class UserStyleSchema(
     public val userStyleSettings: List<UserStyleSetting>
 ) {
     init {
+        var complicationsUserStyleSettingCount = 0
         var customValueUserStyleSettingCount = 0
         for (setting in userStyleSettings) {
-            if (setting is UserStyleSetting.CustomValueUserStyleSetting) {
-                customValueUserStyleSettingCount++
+            when (setting) {
+                is UserStyleSetting.ComplicationsUserStyleSetting ->
+                    complicationsUserStyleSettingCount++
+
+                is UserStyleSetting.CustomValueUserStyleSetting ->
+                    customValueUserStyleSettingCount++
             }
         }
 
+        // This requirement makes it easier to implement companion editors.
+        require(complicationsUserStyleSettingCount <= 1) {
+            "At most only one ComplicationsUserStyleSetting is allowed"
+        }
+
         // There's a hard limit to how big Schema + UserStyle can be and since this data is sent
         // over bluetooth to the companion there will be performance issues well before we hit
         // that the limit. As a result we want the total size of custom data to be kept small and
         // we are initially restricting there to be at most one CustomValueUserStyleSetting.
-        require(
-            customValueUserStyleSettingCount <= 1
-        ) { "At most only one CustomValueUserStyleSetting is allowed" }
+        require(customValueUserStyleSettingCount <= 1) {
+            "At most only one CustomValueUserStyleSetting is allowed"
+        }
     }
 
     /** @hide */
diff --git a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
index 789ddc8..55a881d 100644
--- a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
+++ b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
@@ -358,7 +358,8 @@
      * The ComplicationsManager listens for style changes with this setting and when a
      * [ComplicationsOption] is selected the overrides are automatically applied. Note its suggested
      * that the default [ComplicationOverlay] (the first entry in the list) does not apply any
-     * overrides.
+     * overrides. Only a single [ComplicationsUserStyleSetting] is permitted in the
+     * [UserStyleSchema].
      *
      * Not to be confused with complication provider selection.
      */
@@ -942,7 +943,8 @@
 
     /**
      * An application specific style setting. This style is ignored by the system editor. This is
-     * expected to be used in conjunction with an on watch face editor.
+     * expected to be used in conjunction with an on watch face editor. Only a single
+     * [ComplicationsUserStyleSetting] is permitted in the [UserStyleSchema].
      */
     public class CustomValueUserStyleSetting : UserStyleSetting {
         internal companion object {
diff --git a/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt b/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
index 3504aa6..359caeb 100644
--- a/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
+++ b/wear/wear-watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
@@ -264,12 +264,35 @@
             userStyleRepository.schema
         )
 
-        val customValue = userStyle.selectedOptions[customStyleSetting]!! as
-            UserStyleSetting.CustomValueUserStyleSetting.CustomValueOption
+        val customValue = userStyle.selectedOptions[customStyleSetting]!! as CustomValueOption
         assertThat(customValue.customValue.decodeToString()).isEqualTo("TEST 123")
     }
 
     @Test
+    public fun userStyle_multiple_ComplicationsUserStyleSetting_notAllowed() {
+        val customStyleSetting1 = CustomValueUserStyleSetting(
+            listOf(WatchFaceLayer.BASE),
+            "default".encodeToByteArray()
+        )
+        val customStyleSetting2 = CustomValueUserStyleSetting(
+            listOf(WatchFaceLayer.BASE),
+            "default".encodeToByteArray()
+        )
+
+        try {
+            UserStyleSchema(
+                listOf(customStyleSetting1, customStyleSetting2)
+            )
+            fail(
+                "Constructing a UserStyleSchema with more than one ComplicationsUserStyleSetting " +
+                    "should fail"
+            )
+        } catch (e: Exception) {
+            // expected
+        }
+    }
+
+    @Test
     public fun userStyle_multiple_CustomValueUserStyleSettings_notAllowed() {
         val customStyleSetting1 = CustomValueUserStyleSetting(
             listOf(WatchFaceLayer.BASE),
@@ -308,8 +331,7 @@
 
         userStyleRepository.userStyle = UserStyle(
             mapOf(
-                customStyleSetting to
-                    CustomValueUserStyleSetting.CustomValueOption("test".encodeToByteArray())
+                customStyleSetting to CustomValueOption("test".encodeToByteArray())
             )
         )
 
@@ -370,9 +392,6 @@
             .isEqualTo("12.3")
         assertThat(LongRangeUserStyleSetting.LongRangeOption(123).toString())
             .isEqualTo("123")
-        assertThat(
-            CustomValueUserStyleSetting.CustomValueOption("test".encodeToByteArray())
-                .toString()
-        ).isEqualTo("test")
+        assertThat(CustomValueOption("test".encodeToByteArray()).toString()).isEqualTo("test")
     }
 }
diff --git a/wear/wear-watchface/build.gradle b/wear/wear-watchface/build.gradle
index 0582637..340abf2 100644
--- a/wear/wear-watchface/build.gradle
+++ b/wear/wear-watchface/build.gradle
@@ -43,8 +43,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(TRUTH)
 
     testImplementation(project(":wear:wear-watchface-complications-rendering"))
diff --git a/wear/wear-watchface/guava/build.gradle b/wear/wear-watchface/guava/build.gradle
index a70be6e..28f0610 100644
--- a/wear/wear-watchface/guava/build.gradle
+++ b/wear/wear-watchface/guava/build.gradle
@@ -34,14 +34,14 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(TRUTH)
     testImplementation(ANDROIDX_TEST_CORE)
     testImplementation(ANDROIDX_TEST_RUNNER)
     testImplementation(ANDROIDX_TEST_RULES)
-    testImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    testImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    testImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    testImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     testImplementation(ROBOLECTRIC)
     testImplementation(TRUTH)
 }
diff --git a/wear/wear/build.gradle b/wear/wear/build.gradle
index a1aa6ae..80eb984 100644
--- a/wear/wear/build.gradle
+++ b/wear/wear/build.gradle
@@ -23,9 +23,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    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
 
     testImplementation(KOTLIN_STDLIB)
     testImplementation(ANDROIDX_TEST_CORE)
diff --git a/webkit/integration-tests/testapp/build.gradle b/webkit/integration-tests/testapp/build.gradle
index fe4acbc..4caa18a 100644
--- a/webkit/integration-tests/testapp/build.gradle
+++ b/webkit/integration-tests/testapp/build.gradle
@@ -32,13 +32,13 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CORE, excludes.espresso)
+    androidTestImplementation(ESPRESSO_CONTRIB, excludes.espresso)
     androidTestImplementation(ESPRESSO_IDLING_RESOURCE)
-    androidTestImplementation(ESPRESSO_WEB, libs.exclude_for_espresso)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+    androidTestImplementation(ESPRESSO_WEB, excludes.espresso)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
     // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
 }
 
 rootProject.tasks.getByName("buildOnServer").dependsOn(project.path + ":assembleRelease")
diff --git a/webkit/webkit/build.gradle b/webkit/webkit/build.gradle
index a640c8d..a52b165 100644
--- a/webkit/webkit/build.gradle
+++ b/webkit/webkit/build.gradle
@@ -36,7 +36,7 @@
     androidTestImplementation("androidx.concurrent:concurrent-futures:1.0.0")
 
     // Hamcrest matchers:
-    androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_for_espresso)
+    androidTestImplementation(ESPRESSO_CONTRIB, excludes.espresso)
 }
 
 ext {
diff --git a/window/window-extensions/build.gradle b/window/window-extensions/build.gradle
index 384720b..5ee89bc 100644
--- a/window/window-extensions/build.gradle
+++ b/window/window-extensions/build.gradle
@@ -37,8 +37,8 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
 }
 
 androidx {
diff --git a/window/window/build.gradle b/window/window/build.gradle
index a1d0366..6329f99 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -74,9 +74,9 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
-    androidTestImplementation(MOCKITO_KOTLIN, libs.exclude_bytebuddy)
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy)
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy)
+    androidTestImplementation(MOCKITO_KOTLIN, excludes.bytebuddy)
     androidTestImplementation(TRUTH)
     androidTestImplementation(compileOnly(project(":window:window-extensions")))
     androidTestImplementation(compileOnly(project(":window:window-sidecar")))
diff --git a/work/workmanager-gcm/build.gradle b/work/workmanager-gcm/build.gradle
index b87f85f..004c634 100644
--- a/work/workmanager-gcm/build.gradle
+++ b/work/workmanager-gcm/build.gradle
@@ -48,8 +48,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     testImplementation(JUNIT)
 }
 
diff --git a/work/workmanager-ktx/build.gradle b/work/workmanager-ktx/build.gradle
index fa3953a..e323c94 100644
--- a/work/workmanager-ktx/build.gradle
+++ b/work/workmanager-ktx/build.gradle
@@ -39,8 +39,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     androidTestImplementation("androidx.room:room-testing:2.2.5")
     testImplementation(JUNIT)
 }
diff --git a/work/workmanager-multiprocess/build.gradle b/work/workmanager-multiprocess/build.gradle
index 76bcadb..596d4cb 100644
--- a/work/workmanager-multiprocess/build.gradle
+++ b/work/workmanager-multiprocess/build.gradle
@@ -50,8 +50,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     androidTestImplementation(project(":internal-testutils-runtime"))
     testImplementation(KOTLIN_STDLIB)
     testImplementation(JUNIT)
diff --git a/work/workmanager-testing/build.gradle b/work/workmanager-testing/build.gradle
index 7018887..745ad5a 100644
--- a/work/workmanager-testing/build.gradle
+++ b/work/workmanager-testing/build.gradle
@@ -38,8 +38,8 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has it"s own MockMaker
 
     testImplementation(ANDROIDX_TEST_CORE)
     testImplementation(JUNIT)
diff --git a/work/workmanager/build.gradle b/work/workmanager/build.gradle
index 2ba9ca2..c97b230 100644
--- a/work/workmanager/build.gradle
+++ b/work/workmanager/build.gradle
@@ -75,8 +75,8 @@
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(MOCKITO_CORE, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, excludes.bytebuddy) // DexMaker has its own MockMaker
     androidTestImplementation(project(":internal-testutils-runtime"))
     testImplementation(JUNIT)
     lintPublish(project(":work:work-runtime-lint"))