Merge changes from topic "index_order" into androidx-main

* changes:
  Use enums for @Index#Order API
  Workaround KSP enum annotation value inconsistency.
  Adding support for column index order in @Index
diff --git a/activity/activity-compose/build.gradle b/activity/activity-compose/build.gradle
index c9c5b9e..5bd8c97 100644
--- a/activity/activity-compose/build.gradle
+++ b/activity/activity-compose/build.gradle
@@ -26,13 +26,13 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
 
     implementation(libs.kotlinStdlib)
-    api(projectOrArtifact(":compose:runtime:runtime"))
-    api(projectOrArtifact(":compose:runtime:runtime-saveable"))
+    api("androidx.compose.runtime:runtime:1.0.1")
+    api("androidx.compose.runtime:runtime-saveable:1.0.1")
     api(projectOrArtifact(":activity:activity-ktx"))
-    api("androidx.compose.ui:ui:1.0.0-rc02")
+    api("androidx.compose.ui:ui:1.0.1")
 
     androidTestImplementation projectOrArtifact(":compose:ui:ui-test-junit4")
     androidTestImplementation projectOrArtifact(":compose:material:material")
diff --git a/activity/activity-compose/samples/build.gradle b/activity/activity-compose/samples/build.gradle
index db3752a..4a7171c 100644
--- a/activity/activity-compose/samples/build.gradle
+++ b/activity/activity-compose/samples/build.gradle
@@ -25,14 +25,14 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
     implementation(libs.kotlinStdlib)
 
     compileOnly projectOrArtifact(":annotation:annotation-sampled")
-    implementation "androidx.compose.foundation:foundation:1.0.0-rc02"
+    implementation "androidx.compose.foundation:foundation:1.0.1"
     implementation projectOrArtifact(":activity:activity-compose")
     implementation projectOrArtifact(":activity:activity-ktx")
-    implementation "androidx.compose.material:material:1.0.0-rc02"
+    implementation "androidx.compose.material:material:1.0.1"
 }
 
 androidx {
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/FileLinkingRule.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/FileLinkingRule.kt
new file mode 100644
index 0000000..072e1d0f
--- /dev/null
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/FileLinkingRule.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.benchmark.macro
+
+import androidx.benchmark.InstrumentationResults
+import androidx.benchmark.Outputs
+import androidx.benchmark.macro.perfetto.UiState
+import androidx.benchmark.macro.perfetto.appendUiState
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import java.io.File
+
+/**
+ * Rule to enable linking files and traces to Studio UI for macrobench correctness tests.
+ *
+ * Filepaths are registered, and reported, but files are not created by this class, that should
+ * be handled by the test. Ensure you don't clean up the file - it needs to persist to be copied
+ * over by Studio.
+ */
+class FileLinkingRule : TestRule {
+    private lateinit var currentDescription: Description
+    private var summaryString = ""
+
+    private fun createReportedFilePath(
+        label: String,
+        @Suppress("SameParameterValue") extension: String,
+    ): String {
+        // remove parens / brackets, as it confuses linking
+        val methodLabel = currentDescription.toUniqueName()
+            .replace("(", "_")
+            .replace(")", "_")
+            .replace("[", "_")
+            .replace("]", "_")
+
+        val file = File(
+            Outputs.dirUsableByAppAndShell,
+            "${label}_${Outputs.dateToFileName()}.$extension"
+        )
+        val absolutePath: String = file.absolutePath
+        val relativePath = Outputs.relativePathFor(absolutePath)
+
+        summaryString += "$methodLabel [$label](file://$relativePath)\n"
+        return absolutePath
+    }
+
+    /**
+     * Map of trace abs path -> process to highlight.
+     *
+     * After trace is complete (at end of test), we write a UI state packet to it, so trace UI
+     * can highlight/select the relevant process.
+     */
+    private val traceToPackageMap = mutableMapOf<String, String>()
+
+    fun createReportedTracePath(
+        packageName: String,
+        label: String = "trace"
+    ): String {
+        val absolutePath = createReportedFilePath(label, "perfetto-trace")
+        traceToPackageMap[absolutePath] = packageName
+        return absolutePath
+    }
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return RuleChain
+            .outerRule(::applyInternal)
+            .apply(base, description)
+    }
+
+    private fun applyInternal(base: Statement, description: Description) = object : Statement() {
+        override fun evaluate() {
+            require(Outputs.outputDirectory == Outputs.dirUsableByAppAndShell) {
+                "FileLinkingRule may only be used when outputDirectory == dirUsableByAppAndShell"
+            }
+
+            currentDescription = description
+            try {
+                base.evaluate()
+            } finally {
+                flush()
+            }
+        }
+    }
+
+    private fun flush() {
+        traceToPackageMap.forEach { entry ->
+            File(entry.key).apply {
+                if (exists()) {
+                    appendUiState(
+                        UiState(null, null, entry.value)
+                    )
+                }
+            }
+        }
+
+        InstrumentationResults.instrumentationReport {
+            ideSummaryRecord(
+                summaryV1 = "", // not supported
+                summaryV2 = summaryString.trim()
+            )
+        }
+    }
+
+    private fun Description.toUniqueName() = testClass.simpleName + "_" + methodName
+}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
index b2843e0..2c15264 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Intent
 import android.content.pm.PackageManager
+import androidx.benchmark.macro.perfetto.isShellSessionRooted
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -37,38 +38,38 @@
 @SdkSuppress(minSdkVersion = 27) // Lowest version validated
 @RunWith(AndroidJUnit4::class)
 @LargeTest
-public class MacrobenchmarkScopeTest {
+class MacrobenchmarkScopeTest {
     @Before
     fun setup() {
         // validate target is installed with clear error message,
         // since error messages from e.g. startActivityAndWait may be less clear
         try {
             val pm = InstrumentationRegistry.getInstrumentation().context.packageManager
-            pm.getApplicationInfo(TARGET_PACKAGE_NAME, 0)
+            pm.getApplicationInfo(Packages.TARGET, 0)
         } catch (notFoundException: PackageManager.NameNotFoundException) {
             throw IllegalStateException(
-                "Unable to find target $TARGET_PACKAGE_NAME, is it installed?"
+                "Unable to find target ${Packages.TARGET}, is it installed?"
             )
         }
     }
 
     @Test
-    public fun killTest() {
-        val scope = MacrobenchmarkScope(TARGET_PACKAGE_NAME, launchWithClearTask = true)
+    fun killTest() {
+        val scope = MacrobenchmarkScope(Packages.TARGET, launchWithClearTask = true)
         scope.pressHome()
         scope.startActivityAndWait()
-        assertTrue(isProcessAlive(TARGET_PACKAGE_NAME))
+        assertTrue(isProcessAlive(Packages.TARGET))
         scope.killProcess()
-        assertFalse(isProcessAlive(TARGET_PACKAGE_NAME))
+        assertFalse(isProcessAlive(Packages.TARGET))
     }
 
     @Test
-    public fun compile_speedProfile() {
-        val scope = MacrobenchmarkScope(TARGET_PACKAGE_NAME, launchWithClearTask = true)
+    fun compile_speedProfile() {
+        val scope = MacrobenchmarkScope(Packages.TARGET, launchWithClearTask = true)
         val iterations = 1
         var executions = 0
         val compilation = CompilationMode.SpeedProfile(warmupIterations = iterations)
-        compilation.compile(TARGET_PACKAGE_NAME) {
+        compilation.compile(Packages.TARGET) {
             executions += 1
             scope.pressHome()
             scope.startActivityAndWait()
@@ -77,25 +78,24 @@
     }
 
     @Test
-    public fun compile_speed() {
+    fun compile_speed() {
         val compilation = CompilationMode.Speed
-        compilation.compile(TARGET_PACKAGE_NAME) {
+        compilation.compile(Packages.TARGET) {
             fail("Should never be called for $compilation")
         }
     }
 
     @Test
-    public fun startActivityAndWait_activityNotExported() {
-        val scope = MacrobenchmarkScope(TARGET_PACKAGE_NAME, launchWithClearTask = true)
+    fun startActivityAndWait_activityNotExported() {
+        val scope = MacrobenchmarkScope(Packages.TARGET, launchWithClearTask = true)
         scope.pressHome()
 
         val intent = Intent()
-        intent.setPackage(TARGET_PACKAGE_NAME)
-        intent.action = "$TARGET_PACKAGE_NAME.NOT_EXPORTED_ACTIVITY"
+        intent.setPackage(Packages.TARGET)
+        intent.action = "${Packages.TARGET}.NOT_EXPORTED_ACTIVITY"
 
         val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
-        val prop = device.executeShellCommand("getprop service.adb.root").trim()
-        if (prop == "1") {
+        if (device.isShellSessionRooted()) {
             // while device and adb session are both rooted, doesn't throw
             scope.startActivityAndWait(intent)
         } else {
@@ -112,13 +112,13 @@
     }
 
     @Test
-    public fun startActivityAndWait_invalidActivity() {
-        val scope = MacrobenchmarkScope(TARGET_PACKAGE_NAME, launchWithClearTask = true)
+    fun startActivityAndWait_invalidActivity() {
+        val scope = MacrobenchmarkScope(Packages.TARGET, launchWithClearTask = true)
         scope.pressHome()
 
         val intent = Intent()
         intent.setPackage("this.is.not.a.real.package")
-        intent.action = "$TARGET_PACKAGE_NAME.NOT_EXPORTED_ACTIVITY"
+        intent.action = "${Packages.TARGET}.NOT_EXPORTED_ACTIVITY"
 
         // should throw, unable to resolve Intent
         val exceptionMessage = assertFailsWith<IllegalStateException> {
@@ -129,8 +129,11 @@
     }
 
     @Test
-    public fun startActivityAndWait_sameActivity() {
-        val scope = MacrobenchmarkScope(LOCAL_PACKAGE_NAME, launchWithClearTask = true)
+    fun startActivityAndWait_sameActivity() {
+        val scope = MacrobenchmarkScope(
+            Packages.TEST, // self-instrumenting macrobench, so don't kill the process!
+            launchWithClearTask = true
+        )
         val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
         // Launch first activity, and validate it is displayed
@@ -160,13 +163,4 @@
     private fun isProcessAlive(packageName: String): Boolean {
         return processes().any { it.contains(packageName) }
     }
-
-    public companion object {
-        // Separate target app. Use this app/package if killing/compiling target process.
-        private const val TARGET_PACKAGE_NAME =
-            "androidx.benchmark.integration.macrobenchmark.target"
-
-        // This test app. Use this app/package if not killing/compiling target.
-        private const val LOCAL_PACKAGE_NAME = "androidx.benchmark.macro.test"
-    }
 }
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt
index 8f8e211..bd28aa2 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt
@@ -19,18 +19,15 @@
 import androidx.benchmark.MetricResult
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertNotNull
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-public class MetricResultExtensionsTest {
+class MetricResultExtensionsTest {
     @Test
-    public fun mergeToMetricResults_trivial() {
+    fun mergeToMetricResults_trivial() {
         assertEquals(
             expected = listOf(
                 // note, bar sorted first
@@ -44,7 +41,7 @@
     }
 
     @Test
-    public fun mergeToMetricResults_standard() {
+    fun mergeToMetricResults_standard() {
         assertEquals(
             expected = listOf(
                 // note, bar sorted first
@@ -60,19 +57,19 @@
     }
 
     @Test
-    public fun mergeToMetricResults_missingKey() {
-        val exception = assertFailsWith<IllegalStateException> {
-            listOf(
+    fun mergeToMetricResults_missingKey() {
+        assertEquals(
+            expected = listOf(
+                MetricResult("bar", longArrayOf(101, 201)),
+                MetricResult("foo", longArrayOf(100, 200))
+            ),
+            actual = listOf(
                 mapOf("foo" to 100L, "bar" to 101L),
-                mapOf("foo" to 300L), // bar missing! Time to crash!
+                mapOf("foo" to 300L), // bar missing! Skip this iteration!
                 mapOf("foo" to 200L, "bar" to 201L),
             ).mergeToMetricResults(
                 tracePaths = listOf("trace1.trace", "trace2.trace", "trace3.trace")
             )
-        }
-        val message = exception.message
-        assertNotNull(message)
-        assertTrue(message.contains("Iteration 1 missing keys [bar]"))
-        assertTrue(message.contains("trace1.trace\ntrace2.trace\ntrace3.trace"))
+        )
     }
 }
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/Packages.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/Packages.kt
new file mode 100644
index 0000000..7cc468e
--- /dev/null
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/Packages.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.macro
+
+object Packages {
+    /**
+     * Separate target app.
+     *
+     * Use this app/package if it's necessary to kill/compile target process.
+     */
+    const val TARGET =
+        "androidx.benchmark.integration.macrobenchmark.target"
+
+    /**
+     * This test app - this process.
+     *
+     * Preferably use this app/package if not killing/compiling target.
+     */
+    const val TEST = "androidx.benchmark.macro.test"
+}
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/AtraceTagTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/AtraceTagTest.kt
new file mode 100644
index 0000000..4dec363
--- /dev/null
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/AtraceTagTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.benchmark.macro.perfetto
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import org.junit.Assume
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertContains
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 21)
+@SmallTest
+class AtraceTagTest {
+    private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    private val shellSessionRooted = device.isShellSessionRooted()
+
+    @Test
+    fun atraceListCategories_readable() {
+        val results = device.executeShellCommand("atrace --list_categories")
+        assertNotEquals("", results)
+    }
+
+    private fun getActualSupportedTags(): Set<String> {
+        val results = device.executeShellCommand("atrace --list_categories")
+
+        assertNotEquals("", results)
+        val actualSupportedTags = results
+            .split("\n")
+            .map {
+                println("captured $it")
+                it.trim().split(" ").first()
+            }
+            .filter { it.isNotBlank() }
+            .toSet()
+
+        // verify able to read stdout with guaranteed tag
+        assertContains(actualSupportedTags, "view")
+
+        return actualSupportedTags
+    }
+
+    @Test
+    fun atraceListCategories_unsupported() {
+        val actualSupportedTags = getActualSupportedTags()
+
+        val expectedUnsupportedTags = AtraceTag.unsupported(rooted = shellSessionRooted)
+        val unexpectedTags = expectedUnsupportedTags.intersect(actualSupportedTags)
+        assertEquals(setOf(), unexpectedTags, "Tags expected to be unsupported weren't")
+    }
+
+    @Test
+    fun atraceListCategories_supported() {
+        val actualSupportedTags = getActualSupportedTags()
+        val expectedSupportedTags = AtraceTag.supported(rooted = shellSessionRooted)
+            .map { it.tag }
+            .toSet()
+
+        val missingTags = expectedSupportedTags - actualSupportedTags
+        assertEquals(setOf(), missingTags, "Tags expected to be supported weren't")
+    }
+
+    @Test
+    fun shellSession_root() {
+        Assume.assumeTrue(shellSessionRooted)
+    }
+
+    @Test
+    fun shellSession_unroot() {
+        Assume.assumeFalse(shellSessionRooted)
+    }
+}
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
index b8cbc9f..fccb981 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
@@ -17,7 +17,8 @@
 package androidx.benchmark.macro.perfetto
 
 import android.os.Build
-import androidx.benchmark.Outputs
+import androidx.benchmark.macro.FileLinkingRule
+import androidx.benchmark.macro.Packages
 import androidx.benchmark.macro.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -28,29 +29,31 @@
 import org.junit.Assert.assertTrue
 import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import java.io.File
+import kotlin.test.assertEquals
 
 @SdkSuppress(minSdkVersion = 29) // Lower to 21 after fixing trace config.
 @RunWith(Parameterized::class)
-public class PerfettoCaptureTest(private val unbundled: Boolean) {
-    private val traceFile = File(Outputs.dirUsableByAppAndShell, "PerfettoCaptureTest.trace")
-    private val traceFilePath = traceFile.absolutePath
+class PerfettoCaptureTest(private val unbundled: Boolean) {
+    @get:Rule
+    val linkRule = FileLinkingRule()
 
     @Before
     @After
-    public fun cleanup() {
+    fun cleanup() {
         PerfettoCapture(unbundled).cancel()
-        traceFile.delete()
     }
 
     @LargeTest
     @Test
-    public fun traceAndCheckFileSize() {
+    fun captureAndValidateTrace() {
         // Change the check to API >=21, once we have the correct Perfetto config.
         assumeTrue(Build.VERSION.SDK_INT >= 29 && isAbiSupported())
+
+        val traceFilePath = linkRule.createReportedTracePath(Packages.TEST)
         val perfettoCapture = PerfettoCapture(unbundled)
 
         verifyTraceEnable(false)
@@ -59,27 +62,40 @@
 
         verifyTraceEnable(true)
 
-        trace("PerfettoCaptureTest") {
+        // TODO: figure out why this sleep (200ms+) is needed - possibly related to b/194105203
+        Thread.sleep(500)
+
+        trace(TRACE_SECTION_LABEL) {
             // Tracing non-trivial duration for manual debugging/verification
             Thread.sleep(20)
         }
 
         perfettoCapture.stop(traceFilePath)
 
-        val length = traceFile.length()
-        assertTrue("Expect > 10KiB file, was $length bytes", length > 10 * 1024)
+        val matchingSlices = PerfettoTraceProcessor.querySlices(
+            absoluteTracePath = traceFilePath,
+            TRACE_SECTION_LABEL
+        )
+
+        assertEquals(1, matchingSlices.size)
+        matchingSlices.first().apply {
+            assertEquals(TRACE_SECTION_LABEL, name)
+            assertTrue(dur > 15_000_000) // should be at least 15ms
+        }
     }
 
-    public companion object {
+    companion object {
+        const val TRACE_SECTION_LABEL = "PerfettoCaptureTest"
+
         @Parameterized.Parameters(name = "unbundled={0}")
         @JvmStatic
-        public fun parameters(): Array<Any> {
+        fun parameters(): Array<Any> {
             return arrayOf(true, false)
         }
     }
 }
 
-public fun verifyTraceEnable(enabled: Boolean) {
+fun verifyTraceEnable(enabled: Boolean) {
     // We poll here, since we may need to wait for enable flags to propagate to apps
     verifyWithPolling(
         "Timeout waiting for Trace.isEnabled == $enabled",
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoConfigTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoConfigTest.kt
index f8e1c77..7259dac 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoConfigTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoConfigTest.kt
@@ -21,7 +21,11 @@
 import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
+import perfetto.protos.DataSourceConfig
+import perfetto.protos.FtraceConfig
+import perfetto.protos.TraceConfig
 import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
 import kotlin.test.assertFalse
 import kotlin.test.assertNotNull
 
@@ -29,7 +33,7 @@
 @SmallTest
 class PerfettoConfigTest {
     @Test
-    public fun ftraceBasics() {
+    fun ftraceBasics() {
         val ftraceDataSource =
             PERFETTO_CONFIG.data_sources.first { it.config?.name == "linux.ftrace" }
 
@@ -44,4 +48,37 @@
         assertFalse(ftraceConfig.atrace_categories.contains("webview"))
         assertFalse(ftraceConfig.atrace_categories.contains("memory"))
     }
+
+    @Test
+    fun validateAndEncode() {
+        // default config shouldn't throw
+        PERFETTO_CONFIG.validateAndEncode()
+    }
+
+    @Test
+    fun validateAndEncode_invalidAtraceCategories() {
+        val invalidConfig = TraceConfig(
+            buffers = listOf(
+                TraceConfig.BufferConfig(
+                    size_kb = 16384,
+                    fill_policy = TraceConfig.BufferConfig.FillPolicy.RING_BUFFER
+                )
+            ),
+            data_sources = listOf(
+                TraceConfig.DataSource(
+                    config = DataSourceConfig(
+                        name = "linux.ftrace",
+                        target_buffer = 0,
+                        ftrace_config = FtraceConfig(
+                            atrace_categories = listOf("bad_category")
+                        ),
+                    )
+                )
+            )
+        )
+        val exception = assertFailsWith<IllegalStateException> {
+            invalidConfig.validateAndEncode()
+        }
+        assertTrue(exception.message!!.contains("bad_category"))
+    }
 }
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/ShellUtilsTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/ShellUtilsTest.kt
index f2a3485..90bc15a 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/ShellUtilsTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/ShellUtilsTest.kt
@@ -28,19 +28,20 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-public class ShellUtilsTest {
+class ShellUtilsTest {
     private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
 
     @SdkSuppress(minSdkVersion = 23) // broken below 23
     @Test
-    public fun trivial() {
+    fun trivial() {
         assertEquals("foo\n", device.executeShellScript("echo foo"))
 
         assertEquals(ShellOutput("foo\n", ""), device.executeShellScriptWithStderr("echo foo"))
     }
 
+    @SdkSuppress(minSdkVersion = 23) // broken below 23
     @Test
-    public fun trivialStderr() {
+    fun trivialStderr() {
         val shellOutput = device.executeShellScriptWithStderr("invalidCommand")
 
         assertEquals("", shellOutput.stdout)
@@ -58,7 +59,7 @@
 
     @SdkSuppress(minSdkVersion = 26) // xargs only available before 26
     @Test
-    public fun pipe_xargs() {
+    fun pipe_xargs() {
         // validate piping works with xargs
         assertEquals("foo\n", device.executeShellScript("echo foo | xargs echo $1"))
     }
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt
index 49fb9f4..10094cb 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt
@@ -16,38 +16,29 @@
 
 package androidx.benchmark.macro
 
+import android.util.Log
 import androidx.benchmark.MetricResult
 
 /**
  * Merge the Map<String, Long> results from each iteration into one List<MetricResult>
  */
 internal fun List<Map<String, Long>>.mergeToMetricResults(
-    tracePaths: List<String>
+    @Suppress("UNUSED_PARAMETER") tracePaths: List<String>
 ): List<MetricResult> {
     val setOfAllKeys = flatMap { it.keys }.toSet()
 
-    // validate each key shows up in each iteration
-    val iterationErrorStrings = mapIndexedNotNull { iteration, iterationResults ->
-        if (iterationResults.keys != setOfAllKeys) {
-            "Iteration $iteration missing keys " + (setOfAllKeys - iterationResults.keys)
-        } else null
-    }
-    if (iterationErrorStrings.isNotEmpty()) {
-        throw IllegalStateException(
-            "Error, different metrics observed in different iterations.\n\n" +
-                iterationErrorStrings.joinToString("\n") +
-                "Please report a bug, and include a logcat capture, and all traces captured by " +
-                "this test run:\n" + tracePaths.joinToString("\n") + "\n" +
-                DeviceInfo.deviceSummaryString
-        )
-    }
-
     // build Map<String, List<Long>>
-    val listResults: Map<String, List<Long>> = setOfAllKeys.map { key ->
-        key to map {
-            it[key] ?: error("Metric $key not observed in iteration")
+    val listResults: Map<String, List<Long>> = setOfAllKeys.associateWith { key ->
+        mapIndexedNotNull { iteration, resultMap ->
+            if (resultMap.keys != setOfAllKeys) {
+                // TODO: assert that metrics are always captured (b/193827052)
+                Log.d(TAG, "Skipping results from iter $iteration, it didn't capture all metrics")
+                null
+            } else {
+                resultMap[key] ?: error("Metric $key not observed in iteration")
+            }
         }
-    }.toMap()
+    }
 
     // transform to List<MetricResult>, sorted by metric name
     return listResults.map { (metricName, values) ->
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/AtraceTag.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/AtraceTag.kt
new file mode 100644
index 0000000..ef21283
--- /dev/null
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/AtraceTag.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.benchmark.macro.perfetto
+
+import android.os.Build
+
+/**
+ * Enum representing set of all atrace tags used by macrobenchmark, and whether they're expected to
+ * be supported on the current device.
+ *
+ * Note that this API assumes API >= 21, as that's the library's min API
+ *
+ * While supported tags could be collected from the local device (e.g. in `AtraceTagTest`), the
+ * intent of this class is to track this information statically.
+ */
+@Suppress("unused") // enums always accessed via values()
+internal enum class AtraceTag(
+    val tag: String
+) {
+    ActivityManager("am"),
+    BinderDriver("binder_driver") {
+        override fun supported(api: Int, rooted: Boolean): Boolean {
+            return api >= 24
+        }
+    },
+    Camera("camera"),
+    Dalvik("dalvik"),
+    Frequency("freq"),
+    Graphics("gfx"),
+    HardwareModules("hal"),
+    Idle("idle"),
+    Input("input"),
+    MemReclaim("memreclaim"),
+    Resources("res"),
+    Scheduling("sched"),
+    Synchronization("sync") {
+        override fun supported(api: Int, rooted: Boolean): Boolean {
+            return rooted || api >= 28
+        }
+    },
+    View("view"),
+    WebView("webview"),
+    WindowManager("wm");
+
+    /**
+     * Return true if the tag is available on the specified api level, with specified shell
+     * session root status.
+     */
+    open fun supported(api: Int, rooted: Boolean): Boolean {
+        return true
+    }
+
+    companion object {
+        fun supported(
+            api: Int = Build.VERSION.SDK_INT,
+            rooted: Boolean
+        ): Set<AtraceTag> {
+            return values()
+                .filter { it.supported(api = api, rooted = rooted) }
+                .toSet()
+        }
+
+        fun unsupported(
+            api: Int = Build.VERSION.SDK_INT,
+            rooted: Boolean
+        ): Set<AtraceTag> {
+            return values().toSet() - supported(api, rooted)
+        }
+    }
+}
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoCapture.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoCapture.kt
index f3c5611..d7a9430 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoCapture.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoCapture.kt
@@ -60,7 +60,7 @@
         val configProtoFile = File(Outputs.dirUsableByAppAndShell, "trace_config.pb")
         try {
             userspaceTrace("write config") {
-                configProtoFile.writeBytes(PERFETTO_CONFIG.encode())
+                configProtoFile.writeBytes(PERFETTO_CONFIG.validateAndEncode())
             }
             userspaceTrace("start perfetto process") {
                 helper.startCollecting(configProtoFile.absolutePath, false)
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoConfig.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoConfig.kt
index 464fd24..1a6d08d 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoConfig.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoConfig.kt
@@ -16,6 +16,9 @@
 
 package androidx.benchmark.macro.perfetto
 
+import android.os.Build
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
 import perfetto.protos.DataSourceConfig
 import perfetto.protos.FtraceConfig
 import perfetto.protos.MeminfoCounters
@@ -54,26 +57,31 @@
                 "lowmemorykiller/lowmemory_kill",
             ),
             atrace_categories = listOf(
-                // atrace categories currently come from default systrace tags
-                "am",
-                "binder_driver",
-                "camera",
-                "dalvik",
-                "freq",
-                "gfx",
-                "hal",
-                "idle",
-                "input",
-                "memreclaim",
-                "res",
-                "sched",
-                "sync",
-                "view",
+                AtraceTag.ActivityManager,
+                AtraceTag.BinderDriver,
+                AtraceTag.Camera,
+                AtraceTag.Dalvik,
+                AtraceTag.Frequency,
+                AtraceTag.Graphics,
+                AtraceTag.HardwareModules,
+                AtraceTag.Idle,
+                AtraceTag.Input,
+                AtraceTag.MemReclaim,
+                AtraceTag.Resources,
+                AtraceTag.Scheduling,
+                AtraceTag.Synchronization,
+                AtraceTag.View,
+                AtraceTag.WindowManager
                 // "webview" not included to workaround b/190743595
-                "wm",
                 // "memory" not included as some Q devices requiring ftrace_event
                 // configuration directly to collect this data. See b/171085599
-            ),
+            ).filter {
+                // filter to only supported tags on unrooted build
+                // TODO: use root-only tags as needed
+                it.supported(api = Build.VERSION.SDK_INT, rooted = false)
+            }.map {
+                it.tag
+            },
             // Trace all apps for now
             atrace_apps = listOf("*")
         )
@@ -137,4 +145,23 @@
         PROCESS_STATS_DATASOURCE,
         LINUX_SYS_STATS_DATASOURCE,
     ),
-)
\ No newline at end of file
+)
+
+internal fun TraceConfig.validateAndEncode(): ByteArray {
+    val ftraceConfig = data_sources
+        .mapNotNull { it.config?.ftrace_config }
+        .first()
+
+    // check tags against known-supported tags based on SDK_INT / root status
+    val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    val supportedTags = AtraceTag.supported(
+        api = Build.VERSION.SDK_INT,
+        rooted = device.isShellSessionRooted()
+    ).map { it.tag }.toSet()
+
+    val unsupportedTags = (ftraceConfig.atrace_categories - supportedTags)
+    check(unsupportedTags.isEmpty()) {
+        "Error - attempted to use unsupported atrace tags: $unsupportedTags"
+    }
+    return encode()
+}
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
index 8fdab8f..2646dad 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
@@ -18,6 +18,7 @@
 
 import android.util.Log
 import androidx.annotation.RequiresApi
+import androidx.benchmark.Outputs
 import androidx.benchmark.macro.device
 import androidx.benchmark.macro.userspaceTrace
 import androidx.test.platform.app.InstrumentationRegistry
@@ -42,10 +43,14 @@
         PerfettoHelper.createExecutable("trace_processor_shell")
     }
 
-    fun getJsonMetrics(absoluteTracePath: String, metric: String): String {
+    private fun validateTracePath(absoluteTracePath: String) {
         require(!absoluteTracePath.contains(" ")) {
             "Trace path must not contain spaces: $absoluteTracePath"
         }
+    }
+
+    fun getJsonMetrics(absoluteTracePath: String, metric: String): String {
+        validateTracePath(absoluteTracePath)
         require(!metric.contains(" ")) {
             "Metric must not contain spaces: $metric"
         }
@@ -69,4 +74,78 @@
         }
         return json
     }
+
+    data class Slice(
+        val name: String,
+        val ts: Long,
+        val dur: Long
+    )
+
+    private fun String.unquote(): String {
+        require(this.first() == '"' && this.last() == '"')
+        return this.substring(1, length - 1)
+    }
+
+    /**
+     * Query a trace for a list of slices - name, timestamp, and duration.
+     */
+    fun querySlices(
+        absoluteTracePath: String,
+        vararg sliceNames: String
+    ): List<Slice> {
+        val whereClause = sliceNames
+            .joinToString(separator = " AND ") {
+                "slice.name = '$it'"
+            }
+
+        val queryResult = rawQuery(
+            absoluteTracePath = absoluteTracePath,
+            query = """
+                SELECT slice.name,ts,dur
+                FROM slice
+                JOIN thread_track ON thread_track.id = slice.track_id
+                WHERE $whereClause
+            """.trimMargin()
+        )
+        val resultLines = queryResult.split("\n")
+
+        if (resultLines.first() != "\"name\",\"ts\",\"dur\"") {
+            throw IllegalStateException("query failed!")
+        }
+
+        // results are in CSV with a header row, and strings wrapped with quotes
+        return resultLines
+            .filter { it.isNotBlank() } // drop blank lines
+            .drop(1) // drop the header row
+            .map {
+                val columns = it.split(",")
+                Slice(
+                    name = columns[0].unquote(),
+                    ts = columns[1].toLong(),
+                    dur = columns[2].toLong()
+                )
+            }
+    }
+
+    private fun rawQuery(
+        absoluteTracePath: String,
+        query: String
+    ): String {
+        validateTracePath(absoluteTracePath)
+
+        val queryFile = File(Outputs.dirUsableByAppAndShell, "trace_processor_query.sql")
+        try {
+            queryFile.writeText(query)
+
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val device = instrumentation.device()
+
+            val command = "$shellPath --query-file ${queryFile.absolutePath} $absoluteTracePath"
+            return userspaceTrace("trace_processor_shell") {
+                device.executeShellCommand(command)
+            }
+        } finally {
+            queryFile.delete()
+        }
+    }
 }
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/ShellUtils.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/ShellUtils.kt
index 89bddae..157ed3a 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/ShellUtils.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/ShellUtils.kt
@@ -163,6 +163,14 @@
     return runnableExecutablePath
 }
 
+/**
+ * Returns true if the shell session is rooted, and thus root commands can be run (e.g. atrace
+ * commands with root-only tags)
+ */
+internal fun UiDevice.isShellSessionRooted(): Boolean {
+    return executeShellCommand("getprop service.adb.root").trim() == "1"
+}
+
 private fun UiDevice.moveToTmpAndMakeExecutable(src: String, dst: String) {
     // Note: we don't check for return values from the below, since shell based file
     // permission errors generally crash our process.
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
index 3cd2d2b..1619e7f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
@@ -80,13 +80,4 @@
             executor.execute { callback.onCaptureCancelled() }
         }
     }
-
-    // TODO(b/186853278): Look into why onComplete isn't called
-    override fun onTotalCaptureResult(
-        requestMetadata: RequestMetadata,
-        frameNumber: FrameNumber,
-        totalCaptureResult: FrameInfo
-    ) {
-        onComplete(requestMetadata, frameNumber, totalCaptureResult)
-    }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExcludedSupportedSizesQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExcludedSupportedSizesQuirk.java
index cf5b9d0..29254f2 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExcludedSupportedSizesQuirk.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExcludedSupportedSizesQuirk.java
@@ -22,6 +22,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.Logger;
+import androidx.camera.core.impl.ImageFormatConstants;
 import androidx.camera.core.impl.Quirk;
 
 import java.util.ArrayList;
@@ -34,13 +35,17 @@
  * <p>
  * An example is the resolution size 4000x3000 which is supported on OnePlus 6, but causes a WYSIWYG
  * issue between preview and image capture. See b/157448499.
+ *
+ * <p>On Huawei P20 Lite, the Preview screen will become too bright when 400x400 or 720x720
+ * Preview resolutions are used together with a large zoom in value. The same symptom happens on
+ * ImageAnalysis. See https://issuetracker.google.com/192129158.
  */
 public class ExcludedSupportedSizesQuirk implements Quirk {
 
     private static final String TAG = "ExcludedSupportedSizesQuirk";
 
     static boolean load() {
-        return isOnePlus6() || isOnePlus6T();
+        return isOnePlus6() || isOnePlus6T() || isHuaweiP20Lite();
     }
 
     private static boolean isOnePlus6() {
@@ -52,6 +57,10 @@
                 Build.DEVICE);
     }
 
+    private static boolean isHuaweiP20Lite() {
+        return "HUAWEI".equalsIgnoreCase(Build.BRAND) && "HWANE".equalsIgnoreCase(Build.DEVICE);
+    }
+
     /**
      * Retrieves problematic supported surface sizes that have to be excluded on the current
      * device, for the given camera id and image format.
@@ -64,6 +73,9 @@
         if (isOnePlus6T()) {
             return getOnePlus6TExcludedSizes(cameraId, imageFormat);
         }
+        if (isHuaweiP20Lite()) {
+            return getHuaweiP20LiteExcludedSizes(cameraId, imageFormat);
+        }
         Logger.w(TAG, "Cannot retrieve list of supported sizes to exclude on this device.");
         return Collections.emptyList();
     }
@@ -87,4 +99,16 @@
         }
         return sizes;
     }
+
+    @NonNull
+    private List<Size> getHuaweiP20LiteExcludedSizes(@NonNull String cameraId, int imageFormat) {
+        final List<Size> sizes = new ArrayList<>();
+        if (cameraId.equals("0")
+                && (imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
+                || imageFormat == ImageFormat.YUV_420_888)) {
+            sizes.add(new Size(720, 720));
+            sizes.add(new Size(400, 400));
+        }
+        return sizes;
+    }
 }
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java
index 748720f..be4979d 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainerTest.java
@@ -18,6 +18,7 @@
 
 import static android.graphics.ImageFormat.JPEG;
 import static android.graphics.ImageFormat.PRIVATE;
+import static android.graphics.ImageFormat.YUV_420_888;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -48,6 +49,8 @@
 
     private static final Size SIZE_4000_3000 = new Size(4000, 3000);
     private static final Size SIZE_4160_3120 = new Size(4160, 3120);
+    private static final Size SIZE_720_720 = new Size(720, 720);
+    private static final Size SIZE_400_400 = new Size(400, 400);
 
     @ParameterizedRobolectricTestRunner.Parameters
     public static Collection<Object[]> data() {
@@ -62,6 +65,14 @@
         data.add(new Object[]{new Config("OnePlus", "OnePlus6T", "0", PRIVATE)});
         data.add(new Object[]{new Config("OnePlus", "OnePlus3", "0", JPEG)});
         data.add(new Object[]{new Config(null, null, "0", JPEG)});
+        // Huawei P20 Lite
+        data.add(new Object[]{
+                new Config("HUAWEI", "HWANE", "0", PRIVATE, SIZE_720_720, SIZE_400_400)});
+        data.add(new Object[]{new Config("HUAWEI", "HWANE", "1", PRIVATE)});
+        data.add(new Object[]{
+                new Config("HUAWEI", "HWANE", "0", YUV_420_888, SIZE_720_720, SIZE_400_400)});
+        data.add(new Object[]{new Config("HUAWEI", "HWANE", "1", YUV_420_888)});
+        data.add(new Object[]{new Config("HUAWEI", "HWANE", "0", JPEG)});
         return data;
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/SafeCloseImageReaderProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/SafeCloseImageReaderProxy.java
index f4dae65..5f8c672 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/SafeCloseImageReaderProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/SafeCloseImageReaderProxy.java
@@ -34,9 +34,9 @@
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private volatile int mOutstandingImages = 0;
+    private int mOutstandingImages = 0;
     @GuardedBy("mLock")
-    private volatile boolean mIsClosed = false;
+    private boolean mIsClosed = false;
 
     // The wrapped instance of ImageReaderProxy
     @GuardedBy("mLock")
@@ -46,7 +46,7 @@
     private final Surface mSurface;
 
     // Called after images are closed to check if the ImageReaderProxy should be closed
-    private ForwardingImageProxy.OnImageCloseListener mImageCloseListener = (image) -> {
+    private final ForwardingImageProxy.OnImageCloseListener mImageCloseListener = (image) -> {
         synchronized (mLock) {
             mOutstandingImages--;
             if (mIsClosed && mOutstandingImages == 0) {
@@ -97,16 +97,14 @@
     @GuardedBy("mLock")
     @Nullable
     private ImageProxy wrapImageProxy(@Nullable ImageProxy imageProxy) {
-        synchronized (mLock) {
-            if (imageProxy != null) {
-                mOutstandingImages++;
-                SingleCloseImageProxy singleCloseImageProxy =
-                        new SingleCloseImageProxy(imageProxy);
-                singleCloseImageProxy.addOnImageCloseListener(mImageCloseListener);
-                return singleCloseImageProxy;
-            } else {
-                return null;
-            }
+        if (imageProxy != null) {
+            mOutstandingImages++;
+            SingleCloseImageProxy singleCloseImageProxy =
+                    new SingleCloseImageProxy(imageProxy);
+            singleCloseImageProxy.addOnImageCloseListener(mImageCloseListener);
+            return singleCloseImageProxy;
+        } else {
+            return null;
         }
     }
 
@@ -117,7 +115,6 @@
      * <p>Once this has been called, no more additional ImageProxy can be acquired from the
      * {@link SafeCloseImageReaderProxy}.
      */
-    @GuardedBy("mLock")
     void safeClose() {
         synchronized (mLock) {
             mIsClosed = true;
diff --git a/camera/camera-video/build.gradle b/camera/camera-video/build.gradle
index 60272a1..9f7c8cc 100644
--- a/camera/camera-video/build.gradle
+++ b/camera/camera-video/build.gradle
@@ -56,6 +56,7 @@
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
     androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestImplementation(project(":camera:camera-lifecycle"))
     androidTestImplementation(project(":camera:camera-testing"))
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index c482c6d..d673a33 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -24,16 +24,15 @@
 import android.util.Log
 import android.util.Size
 import android.view.Surface
-import androidx.camera.camera2.Camera2Config
 import androidx.camera.core.CameraInfo
 import androidx.camera.core.CameraSelector
-import androidx.camera.core.CameraX
 import androidx.camera.core.Preview
 import androidx.camera.core.impl.utils.CameraOrientationUtil
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
-import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.SurfaceTextureProvider
+import androidx.camera.testing.fakes.FakeLifecycleOwner
 import androidx.core.util.Consumer
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.LargeTest
@@ -61,7 +60,7 @@
     val cameraRule = CameraUtil.grantCameraPermissionAndPreTest()
 
     companion object {
-        private const val VIDEO_TIMEOUT = 10_000L
+        private const val VIDEO_TIMEOUT_SEC = 10L
         private const val TAG = "VideoRecordingTest"
         @JvmStatic
         @Parameterized.Parameters
@@ -75,7 +74,8 @@
 
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
     private val context: Context = ApplicationProvider.getApplicationContext()
-    private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
+    private lateinit var cameraProvider: ProcessCameraProvider
+    private lateinit var lifecycleOwner: FakeLifecycleOwner
     private lateinit var preview: Preview
     private lateinit var cameraInfo: CameraInfo
 
@@ -112,36 +112,34 @@
     @Before
     fun setUp() {
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(cameraSelector.lensFacing!!))
-        // Skip for b/168175357
+        // Skip test for b/168175357
         Assume.assumeFalse(
             "Cuttlefish has MediaCodec dequeueInput/Output buffer fails issue. Unable to test.",
             Build.MODEL.contains("Cuttlefish") && Build.VERSION.SDK_INT == 29
         )
 
-        CameraX.initialize(context, Camera2Config.defaultConfig()).get()
-        cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
-        cameraInfo = cameraUseCaseAdapter.cameraInfo
+        cameraProvider = ProcessCameraProvider.getInstance(context).get()
+        lifecycleOwner = FakeLifecycleOwner()
+        lifecycleOwner.startAndResume()
 
         // Add extra Preview to provide an additional surface for b/168187087.
         preview = Preview.Builder().build()
         // Sets surface provider to preview
         instrumentation.runOnMainSync {
-            preview.setSurfaceProvider(
-                getSurfaceProvider()
-            )
+            preview.setSurfaceProvider(getSurfaceProvider())
+            cameraInfo = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
+                .cameraInfo
         }
     }
 
     @After
     fun tearDown() {
-        if (this::cameraUseCaseAdapter.isInitialized) {
+        if (this::cameraProvider.isInitialized) {
             instrumentation.runOnMainSync {
-                cameraUseCaseAdapter.apply {
-                    removeUseCases(useCases)
-                }
+                cameraProvider.unbindAll()
             }
+            cameraProvider.shutdown()[10, TimeUnit.SECONDS]
         }
-        CameraX.shutdown().get(10, TimeUnit.SECONDS)
     }
 
     @Test
@@ -157,7 +155,7 @@
         latchForVideoRecording = CountDownLatch(5)
 
         instrumentation.runOnMainSync {
-            cameraUseCaseAdapter.addUseCases(listOf(preview, videoCapture))
+            cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, videoCapture)
         }
 
         // Act.
@@ -172,15 +170,13 @@
 
     @Test
     fun getCorrectResolution_when_setSupportedQuality() {
+        // Pre-arrange.
         Assume.assumeTrue(QualitySelector.getSupportedQualities(cameraInfo).isNotEmpty())
-
         val qualityList = QualitySelector.getSupportedQualities(cameraInfo)
-        instrumentation.runOnMainSync {
-            cameraUseCaseAdapter.addUseCases(listOf(preview))
-        }
-
         Log.d(TAG, "CameraSelector: ${cameraSelector.lensFacing}, QualityList: $qualityList ")
+
         qualityList.forEach loop@{ quality ->
+            // Arrange.
             val targetResolution = QualitySelector.getResolution(cameraInfo, quality)
             if (targetResolution == null) {
                 // If targetResolution is null, try next one
@@ -199,7 +195,13 @@
             latchForVideoRecording = CountDownLatch(5)
 
             instrumentation.runOnMainSync {
-                cameraUseCaseAdapter.addUseCases(listOf(videoCapture))
+                cameraProvider.unbindAll()
+                cameraProvider.bindToLifecycle(
+                    lifecycleOwner,
+                    cameraSelector,
+                    preview,
+                    videoCapture
+                )
             }
 
             // Act.
@@ -210,31 +212,65 @@
 
             // Cleanup.
             file.delete()
-            instrumentation.runOnMainSync {
-                cameraUseCaseAdapter.apply {
-                    removeUseCases(listOf(videoCapture))
-                }
-            }
         }
     }
 
+    @Test
+    fun stopRecording_when_useCaseUnbind() {
+        // Arrange.
+        val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
+        val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        latchForVideoSaved = CountDownLatch(1)
+        latchForVideoRecording = CountDownLatch(5)
+
+        instrumentation.runOnMainSync {
+            cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, videoCapture)
+        }
+
+        // Act.
+        startVideoRecording(videoCapture, file)
+        instrumentation.runOnMainSync {
+            cameraProvider.unbind(videoCapture)
+        }
+
+        // Verify.
+        // Wait for finalize event to saved file.
+        assertThat(latchForVideoSaved.await(VIDEO_TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue()
+
+        // Check if any error after recording finalized
+        assertWithMessage(TAG + "Finalize with error: ${finalize.error}, ${finalize.cause}.")
+            .that(finalize.hasError()).isFalse()
+
+        // Cleanup.
+        file.delete()
+    }
+
+    // TODO(b/193385037): Add test to check video-recording stop when lifecylce state is paused.
+
+    private fun startVideoRecording(videoCapture: VideoCapture<Recorder>, file: File):
+        ActiveRecording {
+            val outputOptions = FileOutputOptions.builder().setFile(file).build()
+
+            val activeRecording = videoCapture.output
+                .prepareRecording(outputOptions)
+                .withEventListener(
+                    CameraXExecutors.directExecutor(),
+                    videoRecordEventListener
+                )
+                .start()
+
+            // Wait for status event to proceed recording for a while.
+            assertThat(latchForVideoRecording.await(VIDEO_TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue()
+
+            return activeRecording
+        }
+
     private fun completeVideoRecording(videoCapture: VideoCapture<Recorder>, file: File) {
-        val outputOptions = FileOutputOptions.builder().setFile(file).build()
-
-        val activeRecording = videoCapture.output
-            .prepareRecording(outputOptions)
-            .withEventListener(
-                CameraXExecutors.directExecutor(),
-                videoRecordEventListener
-            )
-            .start()
-
-        // Wait for status event to proceed recording for a while.
-        assertThat(latchForVideoRecording.await(VIDEO_TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+        val activeRecording = startVideoRecording(videoCapture, file)
 
         activeRecording.stop()
         // Wait for finalize event to saved file.
-        assertThat(latchForVideoSaved.await(VIDEO_TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+        assertThat(latchForVideoSaved.await(VIDEO_TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue()
 
         // Check if any error after recording finalized
         assertWithMessage(TAG + "Finalize with error: ${finalize.error}, ${finalize.cause}.")
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java b/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java
index cb37955..9ac9a24 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java
@@ -101,14 +101,19 @@
      * {@link RecordingStats#AUDIO_DISABLED} for all {@link RecordingStats} send to the listener
      * set by {@link #withEventListener(Executor, Consumer)}.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#RECORD_AUDIO}.
+     * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
+     * permission; without it, recording will fail at {@link #start()} with an
+     * {@link IllegalStateException}.
      *
      * @return this pending recording
+     * @throws IllegalStateException if the {@link Recorder} this recording is associated to
+     * doesn't support audio.
      */
     @RequiresPermission(Manifest.permission.RECORD_AUDIO)
     @NonNull
     public PendingRecording withAudioEnabled() {
+        Preconditions.checkState(mRecorder.isAudioSupported(), "The Recorder this recording is "
+                + "associated to doesn't support audio.");
         mAudioEnabled = true;
         return this;
     }
@@ -134,7 +139,8 @@
      * {@link ActiveRecording} will be in a finalized state, and all controls will be no-ops.
      *
      * @throws IllegalStateException if the associated Recorder currently has an unfinished
-     * active recording.
+     * active recording, or if the recording has {@link #withAudioEnabled()} audio} but
+     * {@link android.Manifest.permission#RECORD_AUDIO} is not granted.
      */
     @NonNull
     public ActiveRecording start() {
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
index e5e52bc9..938cb2b 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
@@ -162,10 +162,6 @@
          */
         INITIALIZING,
         /**
-         * Audio recording is not supported by this Recorder.
-         */
-        UNSUPPORTED,
-        /**
          * Audio recording is disabled for the running recording.
          */
         DISABLED,
@@ -241,9 +237,10 @@
     // May be equivalent to mUserProvidedExecutor or an internal executor if the user did not
     // provide an executor.
     private final Executor mExecutor;
+    private final AtomicBoolean mSurfaceRequested = new AtomicBoolean(false);
+    private final AtomicBoolean mAudioInitialized = new AtomicBoolean(false);
     private SurfaceRequest.TransformationInfo mSurfaceTransformationInfo = null;
     private Throwable mErrorCause;
-    private AtomicBoolean mSurfaceRequested = new AtomicBoolean(false);
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     final SparseArray<CallbackToFutureAdapter.Completer<Void>> mEncodingCompleters =
@@ -294,13 +291,8 @@
         mSequentialExecutor = CameraXExecutors.newSequentialExecutor(mExecutor);
 
         mMediaSpec = MutableStateObservable.withInitialState(composeRecorderMediaSpec(mediaSpec));
-        if (getObservableData(mMediaSpec).getAudioSpec().getChannelCount()
-                == AudioSpec.CHANNEL_COUNT_NONE) {
-            setAudioState(AudioState.UNSUPPORTED);
-        }
     }
 
-    @SuppressLint("MissingPermission")
     @Override
     public void onSurfaceRequested(@NonNull SurfaceRequest request) {
         synchronized (mLock) {
@@ -477,26 +469,18 @@
      * completes. The recording will be considered active, so before it's finalized, an
      * {@link IllegalStateException} will be thrown if this method is called for a second time.
      *
-     * @throws IllegalStateException if there's an active recording or the Recorder has been
-     * released.
+     * @throws IllegalStateException if there's an active recording, or the audio is
+     * {@link PendingRecording#withAudioEnabled() enabled} for the recording but
+     * {@link android.Manifest.permission#RECORD_AUDIO} is not granted.
      */
+    @SuppressLint("MissingPermission")
     @NonNull
     ActiveRecording start(@NonNull PendingRecording pendingRecording) {
         Preconditions.checkNotNull(pendingRecording, "The given PendingRecording cannot be null.");
+        ActiveRecording activeRecording = ActiveRecording.from(pendingRecording);
         synchronized (mLock) {
-            ActiveRecording activeRecording = ActiveRecording.from(pendingRecording);
-            mRunningRecording = activeRecording;
-            switch (getObservableData(mState)) {
-                case RESETTING:
-                    // Fall-through
-                case INITIALIZING:
-                    // The recording will automatically start once the initialization completes.
-                    setState(State.PENDING_RECORDING);
-                    break;
-                case IDLING:
-                    mSequentialExecutor.execute(this::startInternal);
-                    setState(State.RECORDING);
-                    break;
+            State state = getObservableData(mState);
+            switch (state) {
                 case PENDING_PAUSED:
                     // Fall-through
                 case PAUSED:
@@ -505,15 +489,35 @@
                     // Fall-through
                 case RECORDING:
                     throw new IllegalStateException("There's an active recording.");
+                case RESETTING:
+                    // Fall-through
+                case INITIALIZING:
+                    setupAudioIfNeeded(activeRecording);
+                    mRunningRecording = activeRecording;
+                    // The recording will automatically start once the initialization completes.
+                    setState(State.PENDING_RECORDING);
+                    break;
+                case IDLING:
+                    setupAudioIfNeeded(activeRecording);
+                    mRunningRecording = activeRecording;
+                    mSequentialExecutor.execute(this::startInternal);
+                    setState(State.RECORDING);
+                    break;
                 case ERROR:
-                    Logger.e(TAG, "Recording was started when the Recorder had encountered error "
-                            + mErrorCause);
-                    finalizeRecording(VideoRecordEvent.ERROR_RECORDER_ERROR, mErrorCause);
+                    Logger.e(TAG,
+                            "Recording was started when the Recorder had encountered error "
+                                    + mErrorCause);
+                    // Immediately finalize the recording if the Recorder encountered error.
+                    activeRecording.updateVideoRecordEvent(VideoRecordEvent.finalizeWithError(
+                            activeRecording.getOutputOptions(),
+                            getCurrentRecordingStats(),
+                            OutputResults.of(Uri.EMPTY),
+                            VideoRecordEvent.ERROR_RECORDER_ERROR,
+                            mErrorCause));
                     break;
             }
-
-            return activeRecording;
         }
+        return activeRecording;
     }
 
     void pause() {
@@ -646,13 +650,7 @@
     }
 
     @ExecutedBy("mSequentialExecutor")
-    @RequiresPermission(Manifest.permission.RECORD_AUDIO)
     private void initializeInternal(SurfaceRequest surfaceRequest) {
-        if (mAudioState != AudioState.UNSUPPORTED) {
-            // Skip setting up audio as the media spec shows there's no audio channel.
-            setupAudio();
-        }
-
         if (mSurface != null) {
             // The video encoder has already be created, providing the surface directly.
             surfaceRequest.provideSurface(mSurface, mSequentialExecutor, (result) -> {
@@ -769,9 +767,22 @@
                 .build();
     }
 
-    @ExecutedBy("mSequentialExecutor")
     @RequiresPermission(Manifest.permission.RECORD_AUDIO)
-    private void setupAudio() {
+    private void setupAudioIfNeeded(@NonNull ActiveRecording activeRecording) {
+        if (!activeRecording.isAudioEnabled()) {
+            // Skip if audio is not enabled for the recording.
+            return;
+        }
+
+        if (!isAudioSupported()) {
+            throw new IllegalStateException("The Recorder doesn't support recording with audio");
+        }
+
+        if (mAudioInitialized.getAndSet(true)) {
+            // Skip if audio has already been initialized.
+            return;
+        }
+
         MediaSpec mediaSpec = getObservableData(mMediaSpec);
         AudioEncoderConfig config = composeAudioEncoderConfig(mediaSpec);
 
@@ -793,14 +804,7 @@
                     mediaSpec.getAudioSpec());
         } catch (AudioSourceAccessException e) {
             Logger.e(TAG, "Unable to create audio source." + e);
-            setState(State.ERROR);
-            mErrorCause = e;
-            return;
-        } catch (SecurityException e) {
-            Logger.e(TAG, "Missing audio recording permission." + e);
-            setState(State.ERROR);
-            mErrorCause = e;
-            return;
+            throw new IllegalStateException("Unable to create audio source.", e);
         }
 
         mAudioEncoder.setEncoderCallback(new EncoderCallback() {
@@ -878,7 +882,6 @@
         }, mSequentialExecutor);
     }
 
-    @ExecutedBy("mSequentialExecutor")
     @RequiresPermission(Manifest.permission.RECORD_AUDIO)
     @NonNull
     private AudioSource setupAudioSource(@NonNull BufferProvider<InputBuffer> bufferProvider,
@@ -896,8 +899,6 @@
                     @Override
                     public void onSilenced(boolean silenced) {
                         switch (mAudioState) {
-                            case UNSUPPORTED:
-                                // Fall-through
                             case DISABLED:
                                 // Fall-through
                             case ENCODER_ERROR:
@@ -1235,7 +1236,7 @@
     private void resetInternal() {
         if (mAudioEncoder != null) {
             mAudioEncoder.release();
-            mAudioSource = null;
+            mAudioEncoder = null;
         }
         if (mVideoEncoder != null) {
             mVideoEncoder.release();
@@ -1247,13 +1248,12 @@
         }
 
         mSurfaceRequested.set(false);
+        mAudioInitialized.set(false);
         setState(State.INITIALIZING);
     }
 
     private int internalAudioStateToEventAudioState(AudioState audioState) {
         switch (audioState) {
-            case UNSUPPORTED:
-                // Fall-through
             case DISABLED:
                 return RecordingStats.AUDIO_DISABLED;
             case INITIALIZING:
@@ -1271,7 +1271,7 @@
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     boolean isAudioEnabled() {
-        return mAudioState != AudioState.UNSUPPORTED && mAudioState != AudioState.DISABLED;
+        return mAudioState != AudioState.DISABLED && mAudioState != AudioState.ENCODER_ERROR;
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -1320,13 +1320,6 @@
         mRecordingStopError = VideoRecordEvent.ERROR_UNKNOWN;
         mFileSizeLimitInBytes = OutputOptions.FILE_SIZE_UNLIMITED;
 
-        // Reset audio setting to the Recorder default.
-        if (getObservableData(mMediaSpec).getAudioSpec().getChannelCount()
-                == AudioSpec.CHANNEL_COUNT_NONE) {
-            setAudioState(AudioState.UNSUPPORTED);
-        } else {
-            setAudioState(AudioState.INITIALIZING);
-        }
         synchronized (mLock) {
             if (getObservableData(mState) == State.RESETTING) {
                 resetInternal();
@@ -1379,6 +1372,11 @@
         }
     }
 
+    boolean isAudioSupported() {
+        return getObservableData(mMediaSpec).getAudioSpec().getChannelCount()
+                != AudioSpec.CHANNEL_COUNT_NONE;
+    }
+
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     void setState(@NonNull State state) {
         synchronized (mLock) {
@@ -1395,6 +1393,7 @@
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mSequentialExecutor")
     void setAudioState(AudioState audioState) {
         Logger.d(TAG, "Transitioning audio state: " + mAudioState + " --> " + audioState);
         mAudioState = audioState;
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
index 3536ad5..195d892 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.util.Log
 import android.util.Size
+import android.view.Surface
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraX
@@ -47,7 +48,11 @@
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 
-// The integration-tests for MLKit vision barcode component with CameraX ImageAnalysis use case.
+/*  The integration-test is for MLKit vision barcode component with CameraX ImageAnalysis use case.
+    The test is for lab device test. For the local test, mark the @LabTestRule.LabTestFrontCamera,
+    and @LabTestRule.LabTestRearCamera, or enable "setprop log.tag.rearCameraE2E DEBUG" or
+    "setprop log.tag.frontCameraE2E DEBUG" using 'adb shell'.
+*/
 @LargeTest
 @RunWith(Parameterized::class)
 class MLKitBarcodeTest(
@@ -74,6 +79,9 @@
     private lateinit var camera: CameraUseCaseAdapter
     // For MK Kit Barcode scanner
     private lateinit var barcodeScanner: BarcodeScanner
+    private var imageResolution: Size = resolution
+    private var imageRotation: Int = Surface.ROTATION_0
+    private var targetRotation: Int = Surface.ROTATION_0
 
     @Before
     fun setup() {
@@ -103,7 +111,7 @@
     @LabTestRule.LabTestFrontCamera
     @Test
     fun barcodeDetectViaFontCamera() {
-        val imageAnalysis = initImageAnalysis()
+        val imageAnalysis = initImageAnalysis(CameraSelector.LENS_FACING_FRONT)
 
         camera = CameraUtil.createCameraAndAttachUseCase(
             context,
@@ -116,7 +124,7 @@
     @LabTestRule.LabTestRearCamera
     @Test
     fun barcodeDetectViaRearCamera() {
-        val imageAnalysis = initImageAnalysis()
+        val imageAnalysis = initImageAnalysis(CameraSelector.LENS_FACING_BACK)
 
         camera = CameraUtil.createCameraAndAttachUseCase(
             context,
@@ -132,7 +140,8 @@
         imageAnalysis.setAnalyzer(
             CameraXExecutors.ioExecutor()
         ) { imageProxy ->
-            Log.d(TAG, "Process image proxy: $imageProxy")
+            imageResolution = Size(imageProxy.image!!.width, imageProxy.image!!.height)
+            imageRotation = imageProxy.imageInfo.rotationDegrees
             barcodeScanner.process(
                 InputImage.fromMediaImage(
                     imageProxy.image!!,
@@ -154,31 +163,29 @@
                 // received images when finished using them. Otherwise, new images may not be
                 // received or the camera may stall.
                 .addOnCompleteListener {
-                    Log.d(TAG, "Close image proxy: $imageProxy")
                     imageProxy.close()
                 }
         }
 
         // Verify it is the CameraX lab test environment and can detect qr-code.
         assertWithMessage(
-            "Fail to detect qrcode, resolution: $resolution, " +
-                "rearCameraE2E: ${isLoggable(true)}, " +
-                "frontCameraE2E: ${isLoggable(false)} "
+            "Fail to detect qrcode, target resolution: $resolution, " +
+                "image resolution: $imageResolution, " +
+                "target rotation: $targetRotation, " +
+                "image rotation: $imageRotation "
         ).that(latchForBarcodeDetect.await(DETECT_TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
     }
 
-    private fun initImageAnalysis(): ImageAnalysis {
+    private fun initImageAnalysis(lensFacing: Int): ImageAnalysis {
+        val sensorOrientation = CameraUtil.getSensorOrientation(lensFacing)
+        val isRotateNeeded = sensorOrientation!! % 180 != 0
+        Log.d(TAG, "Sensor Orientation: $sensorOrientation, lensFacing: $lensFacing")
+        targetRotation = if (isRotateNeeded) Surface.ROTATION_90 else Surface.ROTATION_0
+
         return ImageAnalysis.Builder()
             .setTargetName("ImageAnalysis")
             .setTargetResolution(resolution)
+            .setTargetRotation(targetRotation)
             .build()
     }
-
-    private fun isLoggable(isRear: Boolean): Boolean {
-        return if (isRear) {
-            Log.isLoggable("rearCameraE2E", Log.DEBUG)
-        } else {
-            Log.isLoggable("frontCameraE2E", Log.DEBUG)
-        }
-    }
 }
diff --git a/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IRendererService.aidl b/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IRendererService.aidl
index ecbf0de..73374bf 100644
--- a/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IRendererService.aidl
+++ b/car/app/app-automotive/src/main/aidl/androidx/car/app/activity/renderer/IRendererService.aidl
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import androidx.car.app.activity.renderer.ICarAppActivity;
+import androidx.car.app.serialization.Bundleable;
 
 /**
  * An interface to be used for communicating with the renderer.
@@ -56,4 +57,14 @@
    * @param serviceName the service that is associated with the activity
    */
   void terminate(in ComponentName serviceName) = 3;
+
+  /**
+   * Performs a handshake, negotiating the api level for communication between app and host.
+   *
+   * @param serviceName       the component for the car app service that the handshake is for
+   * @param appLatestApiLevel the latest api level for the app side
+   *
+   * @return a {@link HandshakeInfo} including the negotiated api level
+   */
+   Bundleable performHandshake(in ComponentName serviceName, int appLatestApiLevel) = 4;
 }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/LogTags.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/LogTags.java
index a3a38c4..b154bfb 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/LogTags.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/LogTags.java
@@ -29,6 +29,8 @@
 public final class LogTags {
     public static final String TAG = androidx.car.app.utils.LogTags.TAG + ".Act";
 
+    public static final String TAG_ERROR = androidx.car.app.utils.LogTags.TAG + ".Error";
+
     private LogTags() {
     }
 }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
index 0022de4..4eaa1d2 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
@@ -35,8 +35,10 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
+import androidx.car.app.HandshakeInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IRendererService;
+import androidx.car.app.versioning.CarAppApiLevels;
 
 import java.util.List;
 
@@ -59,10 +61,15 @@
     private final Context mContext;
     private final ServiceDispatcher mServiceDispatcher;
     private int mDisplayId;
-    @Nullable private Intent mIntent;
-    @Nullable private ICarAppActivity mICarAppActivity;
+    @Nullable
+    private Intent mIntent;
+    @Nullable
+    private ICarAppActivity mICarAppActivity;
+    @Nullable
+    private HandshakeInfo mHandshakeInfo;
 
-    @Nullable IRendererService mRendererService;
+    @Nullable
+    IRendererService mRendererService;
 
     /** A listener receive connection status updates */
     public interface ServiceConnectionListener extends ErrorHandler {
@@ -83,7 +90,8 @@
      * Returns a {@link ServiceDispatcher} that can be used to communicate with the renderer
      * service.
      */
-    @NonNull ServiceDispatcher getServiceDispatcher() {
+    @NonNull
+    ServiceDispatcher getServiceDispatcher() {
         return mServiceDispatcher;
     }
 
@@ -107,6 +115,14 @@
         mRendererService = rendererService;
     }
 
+    /**
+     * Returns the {@link HandshakeInfo} that has been agreed with the host.
+     */
+    @Nullable
+    public HandshakeInfo getHandshakeInfo() {
+        return mHandshakeInfo;
+    }
+
     /** Returns true if the service is currently bound and able to receive messages */
     boolean isBound() {
         return mRendererService != null;
@@ -158,7 +174,7 @@
 
                     // Host rejected the binding.
                     Log.i(LogTags.TAG, "Host service " + name + " rejected the binding "
-                                    + "request");
+                            + "request");
                     mListener.onError(ErrorHandler.ErrorType.HOST_INCOMPATIBLE);
                 }
             };
@@ -236,6 +252,13 @@
         IRendererService rendererService = requireNonNull(mRendererService);
         ComponentName serviceComponentName = requireNonNull(mServiceComponentName);
 
+        // If the host does not support the getHandshakeInfo API, return oldest as it means to
+        // communicate at minimum level.
+        mHandshakeInfo = mServiceDispatcher.fetchNoFail("performHandshake",
+                new HandshakeInfo("", CarAppApiLevels.getOldest()),
+                () -> (HandshakeInfo) rendererService.performHandshake(serviceComponentName,
+                        CarAppApiLevels.getLatest()).get());
+
         Boolean success = mServiceDispatcher.fetch("initialize", false,
                 () -> rendererService.initialize(carAppActivity,
                         serviceComponentName, mDisplayId));
@@ -244,6 +267,7 @@
             mListener.onError(ErrorHandler.ErrorType.HOST_ERROR);
             return;
         }
+
         if (!updateIntent()) {
             return;
         }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceDispatcher.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceDispatcher.java
index 0ee6769..8f2a450 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceDispatcher.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceDispatcher.java
@@ -85,12 +85,15 @@
     }
 
     /**
-     * Retrieves a value from the service. This is a blocking call.
+     * Retrieves a value from the service handling any communication error and displaying the
+     * error to the user.
      *
-     * @param call code to execute to retrieve the value
+     * <p>This is a blocking call
+     *
+     * @param description name for logging purposes
      * @param fallbackValue value to return in case the call is unsuccessful
-     * @param <T> type of value to retrieve
-     * @return the value retrieved
+     * @param call code to execute to retrieve the value
+     * @return the value retrieved or the {@code fallbackValue} if the call failed
      */
     // TODO(b/184697399): Remove two-way calls as these are blocking.
     @Nullable
@@ -102,8 +105,7 @@
         }
         try {
             // TODO(b/184697267): Implement ANR (application not responding) checks
-            T value = call.invoke();
-            return value;
+            return call.invoke();
         } catch (DeadObjectException e) {
             Log.e(LogTags.TAG, "Connection lost", e);
             mErrorHandler.onError(ErrorHandler.ErrorType.HOST_CONNECTION_LOST);
@@ -119,4 +121,35 @@
         }
         return fallbackValue;
     }
+
+    /**
+     * Retrieves a value from the service, ignoring any communication error and just returning
+     * the {@code fallbackValue} if an error is encountered in the communication.
+     *
+     * <p>This is a blocking call
+     *
+     * @param description name for logging purposes
+     * @param fallbackValue value to return in case the call is unsuccessful
+     * @param call code to execute to retrieve the value
+     * @return the value retrieved or the {@code fallbackValue} if the call failed
+     */
+    @Nullable
+    public <T> T fetchNoFail(@NonNull String description, @Nullable T fallbackValue,
+            @NonNull ReturnCall<T> call) {
+        if (!mOnBindingListener.isBound()) {
+            // Avoid dispatching messages if we are not bound to the service
+            return fallbackValue;
+        }
+        try {
+            // TODO(b/184697267): Implement ANR (application not responding) checks
+            return call.invoke();
+        } catch (RemoteException e) {
+            Log.e(LogTags.TAG, "Remote exception (host render service)", e);
+        } catch (BundlerException e) {
+            Log.e(LogTags.TAG, "Bundler exception (protocol)", e);
+        } catch (RuntimeException e) {
+            Log.e(LogTags.TAG, "Runtime exception (unknown)", e);
+        }
+        return fallbackValue;
+    }
 }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java
index ce4389b..7c6e9e7 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java
@@ -17,13 +17,17 @@
 package androidx.car.app.activity.ui;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static androidx.car.app.activity.ErrorHandler.ActionType.UPDATE_HOST;
+import static androidx.car.app.activity.LogTags.TAG_ERROR;
 
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.LinearLayout;
@@ -49,11 +53,15 @@
  */
 @RestrictTo(LIBRARY)
 public final class ErrorMessageView extends LinearLayout {
+
     private static final String VENDING_PACKAGE = "com.android.vending";
     private static final String VENDING_DETAIL_URL =
             "https://play.google.com/store/apps/details?id=";
     private static final String ACTION_RENDER = "android.car.template.host.RendererService";
 
+    // TODO(b/194324567): Remove the hard coded Google templates host package name
+    private static final String HOST_PACKAGE = "com.google.android.apps.automotive.templates.host";
+
     private TextView mErrorMessage;
     private Button mActionButton;
     @Nullable
@@ -85,7 +93,9 @@
         mActionButton.setOnClickListener(v -> onClick());
     }
 
-    /** Updates the error displayed by this view */
+    /**
+     * Updates the error displayed by this view
+     */
     public void setError(@Nullable ErrorHandler.ErrorType errorType) {
         mErrorType = errorType;
         mErrorMessage.setText(mErrorType != null
@@ -95,6 +105,13 @@
                 ? getContext().getString(mErrorType.getActionType().getActionResId())
                 : null);
         mActionButton.setVisibility(mErrorType != null ? View.VISIBLE : View.GONE);
+
+        // If the vending app is not installed, hide the button and update the message.
+        if (mErrorType != null && mErrorType.getActionType() == UPDATE_HOST
+                && !isVendingPackageInstalled()) {
+            mActionButton.setVisibility(INVISIBLE);
+            mErrorMessage.setText(R.string.error_message_no_vending);
+        }
     }
 
     private void onClick() {
@@ -118,27 +135,46 @@
         throw new IllegalArgumentException("Unknown action type: " + mErrorType.getActionType());
     }
 
+    private boolean isVendingPackageInstalled() {
+        try {
+            requireActivity().getPackageManager().getPackageInfo(VENDING_PACKAGE, 0);
+        } catch (NameNotFoundException e) {
+            Log.d(TAG_ERROR, "The vending app not found");
+            return false;
+        }
+        return true;
+    }
+
     private Intent getVendingIntent() {
         Intent rendererIntent = new Intent(ACTION_RENDER);
         List<ResolveInfo> resolveInfoList =
                 requireActivity().getPackageManager().queryIntentServices(
-                    rendererIntent,
-                    PackageManager.GET_META_DATA
-            );
-        // Redirect to the PlayStore package detail if only one package that handles
-        // ACTION_RENDER is found. if found multiple or none, redirect to the PlayStore main
-        // page.
+                        rendererIntent,
+                        PackageManager.GET_META_DATA
+                );
+        // Redirect to the vending app package detail if only one package that handles
+        // ACTION_RENDER is found.
+        // Redirect to GAS host page if found no package that handles ACTION_RENDER.
+        // if found multiple or none, redirect to the vending app main page.
         if (resolveInfoList.size() == 1) {
-            Intent intent = new Intent(Intent.ACTION_VIEW);
-            intent.setPackage(VENDING_PACKAGE);
-            intent.setData(Uri.parse(VENDING_DETAIL_URL
-                    + resolveInfoList.get(0).serviceInfo.packageName));
-            return intent;
+            Log.d(TAG_ERROR, "Find a host, redirect to the page for this host.");
+            return getHostPageIntent(resolveInfoList.get(0).serviceInfo.packageName);
+        } else if (resolveInfoList.size() == 0) {
+            Log.d(TAG_ERROR, "No host found on the device, redirect to GAS host page");
+            return getHostPageIntent(HOST_PACKAGE);
         } else {
+            Log.d(TAG_ERROR, "Multiple host found, redirect to the vending app main page");
             return requireActivity().getPackageManager().getLaunchIntentForPackage(VENDING_PACKAGE);
         }
     }
 
+    private Intent getHostPageIntent(String packageName) {
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setPackage(VENDING_PACKAGE);
+        intent.setData(Uri.parse(VENDING_DETAIL_URL + packageName));
+        return intent;
+    }
+
     private FragmentActivity requireActivity() {
         return (FragmentActivity) getContext();
     }
diff --git a/car/app/app-automotive/src/main/res/values/strings.xml b/car/app/app-automotive/src/main/res/values/strings.xml
index ed4a207..37da928 100644
--- a/car/app/app-automotive/src/main/res/values/strings.xml
+++ b/car/app/app-automotive/src/main/res/values/strings.xml
@@ -39,4 +39,6 @@
     <string name="error_message_multiple_hosts">Incompatible system</string>
     <!-- Message displayed when an unknown error is detected [CHAR_LIMIT=200] -->
     <string name="error_message_unknown_error">Unknown error</string>
+    <!-- Message displayed when an unknown error is detected [CHAR_LIMIT=200] -->
+    <string name="error_message_no_vending">Please contact car services</string>
 </resources>
\ No newline at end of file
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
index a633647..7e75ece 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
@@ -345,37 +345,4 @@
             });
         }
     }
-
-    // Use delegate to forward events to a mock. Mockito interceptor is not maintained on
-    // top-level IBinder after call to IRenderService.Stub.asInterface() in CarAppActivity.
-    private static class RenderServiceDelegate extends IRendererService.Stub {
-        private final IRendererService mService;
-        private ICarAppActivity mCarAppActivity;
-
-        RenderServiceDelegate(IRendererService service) {
-            mService = service;
-        }
-
-        @Override
-        public boolean initialize(ICarAppActivity carActivity, ComponentName serviceName,
-                int displayId) throws RemoteException {
-            mCarAppActivity = carActivity;
-            return mService.initialize(carActivity, serviceName, displayId);
-        }
-
-        @Override
-        public boolean onNewIntent(Intent intent, ComponentName serviceName, int displayId)
-                throws RemoteException {
-            return mService.onNewIntent(intent, serviceName, displayId);
-        }
-
-        @Override
-        public void terminate(ComponentName serviceName) throws RemoteException {
-            mService.terminate(serviceName);
-        }
-
-        public ICarAppActivity getCarAppActivity() {
-            return mCarAppActivity;
-        }
-    }
 }
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/RenderServiceDelegate.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/RenderServiceDelegate.java
new file mode 100644
index 0000000..cccc414
--- /dev/null
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/RenderServiceDelegate.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.car.app.activity;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.RemoteException;
+
+import androidx.car.app.HandshakeInfo;
+import androidx.car.app.activity.renderer.ICarAppActivity;
+import androidx.car.app.activity.renderer.IRendererService;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
+import androidx.car.app.versioning.CarAppApiLevels;
+
+/**
+ * Use delegate to forward events to a mock. Mockito interceptor is not maintained on top-level
+ * IBinder after call to IRenderService.Stub.asInterface() in CarAppActivity.
+ */
+public class RenderServiceDelegate extends IRendererService.Stub {
+    private final IRendererService mService;
+    private ICarAppActivity mCarAppActivity;
+
+    RenderServiceDelegate(IRendererService service) {
+        mService = service;
+    }
+
+    @Override
+    public boolean initialize(ICarAppActivity carActivity, ComponentName serviceName,
+            int displayId) throws RemoteException {
+        mCarAppActivity = carActivity;
+        return mService.initialize(carActivity, serviceName, displayId);
+    }
+
+    @Override
+    public boolean onNewIntent(Intent intent, ComponentName serviceName, int displayId)
+            throws RemoteException {
+        return mService.onNewIntent(intent, serviceName, displayId);
+    }
+
+    @Override
+    public void terminate(ComponentName serviceName) throws RemoteException {
+        mService.terminate(serviceName);
+    }
+
+    @Override
+    public Bundleable performHandshake(ComponentName serviceName, int appLatestApiLevel)
+            throws RemoteException {
+        mService.performHandshake(serviceName, appLatestApiLevel);
+        try {
+            return Bundleable.create(new HandshakeInfo("", CarAppApiLevels.getLatest()));
+        } catch (BundlerException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /** Returns the {@link ICarAppActivity} received in {@link #initialize}. */
+    public ICarAppActivity getCarAppActivity() {
+        return mCarAppActivity;
+    }
+}
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
index 1b7373d..5bfc022 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
@@ -37,8 +37,12 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import androidx.car.app.HandshakeInfo;
 import androidx.car.app.activity.renderer.ICarAppActivity;
 import androidx.car.app.activity.renderer.IRendererService;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
+import androidx.car.app.versioning.CarAppApiLevels;
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Test;
@@ -95,6 +99,8 @@
                     anyInt())).thenReturn(true);
             when(mRenderService.onNewIntent(any(Intent.class), any(ComponentName.class),
                     anyInt())).thenReturn(true);
+            when(mRenderService.performHandshake(any(ComponentName.class), anyInt())).thenReturn(
+                    Bundleable.create(new HandshakeInfo("", CarAppApiLevels.getLatest())));
 
             ShadowApplication sa = shadowOf(app);
             sa.setComponentNameAndServiceForBindService(mRendererComponent, mRenderServiceDelegate);
@@ -188,9 +194,43 @@
                     TEST_DISPLAY_ID);
             verify(renderService).onNewIntent(TEST_INTENT, mFakeCarAppServiceComponent,
                     TEST_DISPLAY_ID);
+            verify(renderService).performHandshake(mFakeCarAppServiceComponent,
+                    CarAppApiLevels.getLatest());
         } catch (RemoteException e) {
             fail(Log.getStackTraceString(e));
         }
+        assertThat(mServiceConnectionManager.getHandshakeInfo().getHostCarAppApiLevel()).isEqualTo(
+                CarAppApiLevels.getLatest());
+        assertThat(mServiceConnectionManager.isBound()).isTrue();
+    }
+
+    @Test
+    public void testBind_hostDoesNotSupportHandshake_usesMinLevel() throws RemoteException {
+        setupCarAppActivityForTesting();
+        ICarAppActivity iCarAppActivity = mock(ICarAppActivity.class);
+
+        IRendererService renderService = createMockRendererService();
+        when(renderService.performHandshake(any(ComponentName.class), anyInt())).thenThrow(
+                new SecurityException());
+
+        mServiceConnectionManager.setRendererService(renderService);
+        mServiceConnectionManager.bind(TEST_INTENT, iCarAppActivity, TEST_DISPLAY_ID);
+        mMainLooper.idle();
+
+        try {
+            assertThat(mViewModel.getState().getValue()).isEqualTo(CarAppViewModel.State.CONNECTED);
+            assertThat(mViewModel.getError().getValue()).isNull();
+            verify(renderService).initialize(iCarAppActivity, mFakeCarAppServiceComponent,
+                    TEST_DISPLAY_ID);
+            verify(renderService).onNewIntent(TEST_INTENT, mFakeCarAppServiceComponent,
+                    TEST_DISPLAY_ID);
+            verify(renderService).performHandshake(mFakeCarAppServiceComponent,
+                    CarAppApiLevels.getLatest());
+        } catch (RemoteException e) {
+            fail(Log.getStackTraceString(e));
+        }
+        assertThat(mServiceConnectionManager.getHandshakeInfo().getHostCarAppApiLevel()).isEqualTo(
+                CarAppApiLevels.getOldest());
         assertThat(mServiceConnectionManager.isBound()).isTrue();
     }
 
@@ -270,33 +310,6 @@
         assertThat(mServiceConnectionManager.isBound()).isFalse();
     }
 
-    // Use delegate to forward events to a mock. Mockito interceptor is not maintained on
-    // top-level IBinder after call to IRenderService.Stub.asInterface() in CarAppActivity.
-    private static class RenderServiceDelegate extends IRendererService.Stub {
-        private final IRendererService mService;
-
-        RenderServiceDelegate(IRendererService service) {
-            mService = service;
-        }
-
-        @Override
-        public boolean initialize(ICarAppActivity carActivity, ComponentName serviceName,
-                int displayId) throws RemoteException {
-            return mService.initialize(carActivity, serviceName, displayId);
-        }
-
-        @Override
-        public boolean onNewIntent(Intent intent, ComponentName serviceName, int displayId)
-                throws RemoteException {
-            return mService.onNewIntent(intent, serviceName, displayId);
-        }
-
-        @Override
-        public void terminate(ComponentName serviceName) throws RemoteException {
-            mService.terminate(serviceName);
-        }
-    }
-
     private IRendererService createMockRendererService() {
         IRendererService renderService = mock(IRendererService.class);
         try {
@@ -305,7 +318,9 @@
                     anyInt())).thenReturn(true);
             when(renderService.onNewIntent(any(Intent.class), any(ComponentName.class),
                     anyInt())).thenReturn(true);
-        } catch (RemoteException e) {
+            when(renderService.performHandshake(any(ComponentName.class), anyInt())).thenReturn(
+                    Bundleable.create(new HandshakeInfo("", CarAppApiLevels.getLatest())));
+        } catch (RemoteException | BundlerException e) {
             fail(Log.getStackTraceString(e));
         }
         return renderService;
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 63cdf9e..44a0afd 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -40,9 +40,9 @@
         api("androidx.annotation:annotation:1.1.0")
 
         implementation(project(":compose:runtime:runtime"))
-        implementation(project(":compose:ui:ui"))
-        implementation(project(":compose:ui:ui-unit"))
-        implementation(project(":compose:ui:ui-util"))
+        implementation("androidx.compose.ui:ui:1.0.0")
+        implementation("androidx.compose.ui:ui-unit:1.0.0")
+        implementation("androidx.compose.ui:ui-util:1.0.0")
         implementation(libs.kotlinStdlib)
         api(libs.kotlinCoroutinesCore)
 
@@ -57,7 +57,7 @@
         androidTestImplementation(libs.testCore)
         androidTestImplementation(libs.junit)
         androidTestImplementation(project(":compose:animation:animation"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.0.0")
         androidTestImplementation(project(":compose:test-utils"))
 
         lintPublish project(":compose:animation:animation-core-lint")
diff --git a/compose/animation/animation-core/samples/build.gradle b/compose/animation/animation-core/samples/build.gradle
index 18ad38d..d69fec1 100644
--- a/compose/animation/animation-core/samples/build.gradle
+++ b/compose/animation/animation-core/samples/build.gradle
@@ -32,10 +32,10 @@
     compileOnly(project(":annotation:annotation-sampled"))
     implementation(project(":compose:animation:animation-core"))
     implementation(project(":compose:runtime:runtime"))
-    implementation(project(":compose:ui:ui"))
-    implementation(project(":compose:ui:ui-unit"))
-    implementation(project(":compose:foundation:foundation"))
-    implementation(project(":compose:material:material"))
+    implementation("androidx.compose.ui:ui:1.0.0")
+    implementation("androidx.compose.ui:ui-unit:1.0.0")
+    implementation("androidx.compose.foundation:foundation:1.0.0")
+    implementation("androidx.compose.material:material:1.0.0")
 }
 
 androidx {
diff --git a/compose/animation/animation-graphics/build.gradle b/compose/animation/animation-graphics/build.gradle
index c222c21..b102bdf 100644
--- a/compose/animation/animation-graphics/build.gradle
+++ b/compose/animation/animation-graphics/build.gradle
@@ -38,12 +38,12 @@
 
         api("androidx.annotation:annotation:1.1.0")
         api(project(":compose:animation:animation"))
-        api(project(":compose:foundation:foundation-layout"))
+        api("androidx.compose.foundation:foundation-layout:1.0.0")
         api(project(":compose:runtime:runtime"))
         api(project(":compose:ui:ui"))
-        api(project(":compose:ui:ui-geometry"))
+        api("androidx.compose.ui:ui-geometry:1.0.0")
 
-        implementation(project(":compose:ui:ui-util"))
+        implementation("androidx.compose.ui:ui-util:1.0.0")
         implementation(libs.kotlinStdlibCommon)
         implementation("androidx.core:core-ktx:1.5.0")
 
@@ -51,8 +51,8 @@
         testImplementation(libs.testRunner)
         testImplementation(libs.junit)
 
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+        androidTestImplementation("androidx.compose.foundation:foundation:1.0.0")
+        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.0.0")
         androidTestImplementation(project(":compose:test-utils"))
         androidTestImplementation(libs.testRules)
         androidTestImplementation(libs.testRunner)
diff --git a/compose/animation/animation-graphics/samples/build.gradle b/compose/animation/animation-graphics/samples/build.gradle
index 6f2593e..8fd9347 100644
--- a/compose/animation/animation-graphics/samples/build.gradle
+++ b/compose/animation/animation-graphics/samples/build.gradle
@@ -33,10 +33,10 @@
 
     implementation(project(":compose:animation:animation"))
     implementation(project(":compose:animation:animation-graphics"))
-    implementation(project(":compose:foundation:foundation"))
-    implementation(project(":compose:material:material"))
+    implementation("androidx.compose.foundation:foundation:1.0.0")
+    implementation("androidx.compose.material:material:1.0.0")
     implementation(project(":compose:runtime:runtime"))
-    implementation(project(":compose:ui:ui-text"))
+    implementation("androidx.compose.ui:ui-text:1.0.0")
 }
 
 androidx {
diff --git a/compose/animation/animation-graphics/src/androidAndroidTest/kotlin/androidx/compose/animation/graphics/vector/AnimatorTest.kt b/compose/animation/animation-graphics/src/androidAndroidTest/kotlin/androidx/compose/animation/graphics/vector/AnimatorTest.kt
new file mode 100644
index 0000000..8e3a241
--- /dev/null
+++ b/compose/animation/animation-graphics/src/androidAndroidTest/kotlin/androidx/compose/animation/graphics/vector/AnimatorTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.graphics.vector
+
+import androidx.compose.animation.core.InternalAnimationApi
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.graphics.vector.VectorProperty
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class AnimatorTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val tolerance = 0.01f
+
+    private fun objectAnimator(
+        propertyName: String,
+        duration: Int,
+        keyframes: List<Keyframe<Float>>,
+        startDelay: Int = 0,
+        repeatCount: Int = 0,
+        repeatMode: RepeatMode = RepeatMode.Restart,
+    ): ObjectAnimator {
+        return ObjectAnimator(
+            duration,
+            startDelay,
+            repeatCount,
+            repeatMode,
+            listOf(PropertyValuesHolderFloat(propertyName, keyframes))
+        )
+    }
+
+    private fun sequentialAnimatorSet(
+        propertyName: String,
+        startValue: Float,
+        durations: List<Int>,
+        toValues: List<Float>,
+        startDelay: Int = 0,
+        repeatCount: Int = 0,
+        repeatMode: RepeatMode = RepeatMode.Restart,
+    ): AnimatorSet {
+        if (durations.size != toValues.size) {
+            throw RuntimeException()
+        }
+        val size = durations.size
+        return AnimatorSet(
+            (0 until size).map { i ->
+                objectAnimator(
+                    propertyName,
+                    durations[i],
+                    listOf(
+                        Keyframe(0f, if (i == 0) startValue else toValues[i - 1], LinearEasing),
+                        Keyframe(1f, toValues[i], LinearEasing),
+                    ),
+                    startDelay,
+                    repeatCount,
+                    repeatMode
+                )
+            },
+            Ordering.Sequentially
+        )
+    }
+
+    @Test
+    fun simpleFloatProperty() {
+        verifyAnimator(
+            objectAnimator(
+                "translateX",
+                1000,
+                listOf(
+                    Keyframe(0f, 0f, LinearEasing),
+                    Keyframe(1f, 1000f, LinearEasing)
+                )
+            )
+        )
+    }
+
+    @Test
+    fun keyframes() {
+        verifyAnimator(
+            objectAnimator(
+                "translateX",
+                1000,
+                listOf(
+                    Keyframe(0f, 0f, LinearEasing),
+                    Keyframe(0.2f, 200f, LinearEasing),
+                    Keyframe(0.5f, 500f, LinearEasing),
+                    Keyframe(0.7f, 700f, LinearEasing),
+                    Keyframe(1f, 1000f, LinearEasing)
+                )
+            )
+        )
+    }
+
+    @Test
+    fun sequentialAnimatorSet() {
+        verifyAnimator(
+            sequentialAnimatorSet(
+                "translateX",
+                0f,
+                listOf(200, 300, 200, 300),
+                listOf(200f, 500f, 700f, 1000f)
+            )
+        )
+    }
+
+    @OptIn(InternalAnimationApi::class, ExperimentalComposeUiApi::class)
+    private fun verifyAnimator(a: Animator) {
+        val isAtEnd = mutableStateOf(false)
+        val config = StateVectorConfig()
+        rule.setContent {
+            val transition = updateTransition(isAtEnd.value, label = "translateX")
+            a.Configure(transition, config, 1000, 0)
+            if (transition.isRunning) {
+                assertThat(config.getOrDefault(VectorProperty.TranslateX, -1f))
+                    .isWithin(tolerance)
+                    .of(
+                        if (transition.targetState) {
+                            transition.playTimeNanos / 1000f / 1000f
+                        } else {
+                            1000f - transition.playTimeNanos / 1000f / 1000f
+                        }
+                    )
+            }
+        }
+        assertThat(config.getOrDefault(VectorProperty.TranslateX, -1f))
+            .isWithin(tolerance)
+            .of(0f)
+        // Start to end
+        rule.runOnIdle { isAtEnd.value = true }
+        rule.waitForIdle()
+        assertThat(config.getOrDefault(VectorProperty.TranslateX, -1f))
+            .isWithin(tolerance)
+            .of(1000f)
+        // End to start
+        rule.runOnIdle { isAtEnd.value = false }
+        rule.waitForIdle()
+        assertThat(config.getOrDefault(VectorProperty.TranslateX, -1f))
+            .isWithin(tolerance)
+            .of(0f)
+    }
+}
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
index 5eb3680..846fe231c 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
@@ -118,13 +118,100 @@
             }
             Ordering.Sequentially -> {
                 var accumulatedDelay = parentDelay
-                animators.fastForEach { animator ->
+                normalizeSequentialAnimators().fastForEach { animator ->
                     animator.Configure(transition, config, overallDuration, accumulatedDelay)
                     accumulatedDelay += animator.totalDuration
                 }
             }
         }
     }
+
+    /**
+     * Normalizes a sequential animator set that is used as a list of keyframes for a single
+     * property. The animators are expected to run one after another animating the same property
+     * continuously, and none of the animators should use intermediate keyframes in it. If the
+     * animators meet this criteria, they are converted to an animator with multiple keyframes.
+     * Otherwise, this returns [animators] as they are.
+     */
+    private fun normalizeSequentialAnimators(): List<Animator> {
+        if (ordering != Ordering.Sequentially) {
+            return animators
+        }
+        var propertyName: String? = null
+        val keyframes = mutableListOf<Keyframe<Any?>>()
+        var startDelay: Int? = null
+        var resultHolder: PropertyValuesHolder<*>? = null
+        var accumulatedDuration = 0f
+        val totalDuration = totalDuration
+        for (animator in animators) {
+            if (animator !is ObjectAnimator) {
+                return animators
+            }
+            if (startDelay == null) {
+                startDelay = animator.startDelay
+            }
+            val holders = animator.holders
+            if (holders.size != 1) {
+                return animators
+            }
+            val holder = holders[0]
+            if (holder !is PropertyValuesHolder1D) {
+                return animators
+            }
+            if (propertyName == null) {
+                propertyName = holder.propertyName
+            } else if (propertyName != holder.propertyName) {
+                return animators
+            }
+            if (resultHolder == null) {
+                @Suppress("UNCHECKED_CAST")
+                resultHolder = when (holder) {
+                    is PropertyValuesHolderFloat -> PropertyValuesHolderFloat(
+                        propertyName,
+                        keyframes as List<Keyframe<Float>>
+                    )
+                    is PropertyValuesHolderInt -> PropertyValuesHolderInt(
+                        propertyName,
+                        keyframes as List<Keyframe<Int>>
+                    )
+                    is PropertyValuesHolderPath -> PropertyValuesHolderPath(
+                        propertyName,
+                        keyframes as List<Keyframe<List<PathNode>>>
+                    )
+                    is PropertyValuesHolderColor -> PropertyValuesHolderColor(
+                        propertyName,
+                        keyframes as List<Keyframe<Color>>
+                    )
+                }
+            }
+            if (holder.animatorKeyframes.size != 2) {
+                return animators
+            }
+            val start = holder.animatorKeyframes[0]
+            val end = holder.animatorKeyframes[1]
+            if (start.fraction != 0f || end.fraction != 1f) {
+                return animators
+            }
+            if (keyframes.isEmpty()) {
+                keyframes.add(Keyframe(0f, start.value, start.interpolator))
+            }
+            accumulatedDuration += animator.duration
+            val fraction = accumulatedDuration / (totalDuration - startDelay)
+            keyframes.add(Keyframe(fraction, end.value, end.interpolator))
+        }
+        if (resultHolder == null) {
+            return animators
+        }
+        return listOf(
+            ObjectAnimator(
+                duration = totalDuration,
+                startDelay = startDelay ?: 0,
+                repeatCount = 0,
+                repeatMode = RepeatMode.Restart,
+                holders = listOf(resultHolder)
+            )
+        )
+    }
 }
 
 internal sealed class PropertyValuesHolder<T> {
@@ -335,7 +422,8 @@
     }
 
     fun interpolate(fraction: Float): List<PathNode> {
-        val index = (animatorKeyframes.indexOfFirst { it.fraction > fraction } - 1).coerceAtLeast(0)
+        val index = (animatorKeyframes.indexOfFirst { it.fraction >= fraction } - 1)
+            .coerceAtLeast(0)
         val easing = animatorKeyframes[index + 1].interpolator
         val innerFraction = easing.transform(
             (
diff --git a/compose/animation/animation/build.gradle b/compose/animation/animation/build.gradle
index 728e201..b204e6e 100644
--- a/compose/animation/animation/build.gradle
+++ b/compose/animation/animation/build.gradle
@@ -38,20 +38,20 @@
 
         api("androidx.annotation:annotation:1.1.0")
         api(project(":compose:animation:animation-core"))
-        api(project(":compose:foundation:foundation-layout"))
+        api("androidx.compose.foundation:foundation-layout:1.0.0")
         api(project(":compose:runtime:runtime"))
-        api(project(":compose:ui:ui"))
-        api(project(":compose:ui:ui-geometry"))
+        api("androidx.compose.ui:ui:1.0.0")
+        api("androidx.compose.ui:ui-geometry:1.0.0")
 
-        implementation(project(":compose:ui:ui-util"))
+        implementation("androidx.compose.ui:ui-util:1.0.0")
         implementation(libs.kotlinStdlibCommon)
 
         testImplementation(libs.testRules)
         testImplementation(libs.testRunner)
         testImplementation(libs.junit)
 
-        androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+        androidTestImplementation("androidx.compose.foundation:foundation:1.0.0")
+        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.0.0")
         androidTestImplementation(project(":compose:test-utils"))
         androidTestImplementation(libs.testRules)
         androidTestImplementation(libs.testRunner)
diff --git a/compose/animation/animation/samples/build.gradle b/compose/animation/animation/samples/build.gradle
index 2423c37..4ea7433 100644
--- a/compose/animation/animation/samples/build.gradle
+++ b/compose/animation/animation/samples/build.gradle
@@ -33,10 +33,10 @@
     compileOnly(project(":annotation:annotation-sampled"))
 
     implementation(project(":compose:animation:animation"))
-    implementation(project(":compose:foundation:foundation"))
-    implementation(project(":compose:material:material"))
+    implementation("androidx.compose.foundation:foundation:1.0.0")
+    implementation("androidx.compose.material:material:1.0.0")
     implementation(project(":compose:runtime:runtime"))
-    implementation(project(":compose:ui:ui-text"))
+    implementation("androidx.compose.ui:ui-text:1.0.0")
 }
 
 androidx {
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index 4f2b011..a11093e 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -45,7 +45,7 @@
             api(project(":compose:material:material"))
             api(project(":compose:runtime:runtime"))
             api(project(":compose:ui:ui"))
-            api(project(":compose:ui:ui-tooling"))
+            api(project(":compose:ui:ui-tooling-preview"))
         }
 
         jvmMain.dependencies {
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index a393eff..d4801bf 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -36,10 +36,10 @@
          */
 
         api("androidx.annotation:annotation:1.1.0")
-        api(project(":compose:ui:ui"))
+        api("androidx.compose.ui:ui:1.0.0")
 
         implementation(project(":compose:runtime:runtime"))
-        implementation(project(":compose:ui:ui-util"))
+        implementation("androidx.compose.ui:ui-util:1.0.0")
         implementation(libs.kotlinStdlibCommon)
 
         testImplementation(libs.testRules)
@@ -47,7 +47,7 @@
         testImplementation(libs.junit)
 
         androidTestImplementation(project(":compose:foundation:foundation"))
-        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.0.0")
         androidTestImplementation(project(":compose:test-utils"))
         androidTestImplementation(project(":activity:activity-compose"))
         androidTestImplementation(libs.testRules)
diff --git a/compose/foundation/foundation-layout/samples/build.gradle b/compose/foundation/foundation-layout/samples/build.gradle
index dc0aefa1..81ada2a 100644
--- a/compose/foundation/foundation-layout/samples/build.gradle
+++ b/compose/foundation/foundation-layout/samples/build.gradle
@@ -32,10 +32,10 @@
     compileOnly(project(":annotation:annotation-sampled"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:foundation:foundation-layout"))
-    implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material:1.0.0")
     implementation(project(":compose:runtime:runtime"))
-    implementation(project(":compose:ui:ui"))
-    implementation(project(":compose:ui:ui-text"))
+    implementation("androidx.compose.ui:ui:1.0.0")
+    implementation("androidx.compose.ui:ui-text:1.0.0")
 }
 
 androidx {
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 6f4feee..c74d86d 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -36,14 +36,15 @@
          * corresponding block above
          */
         api("androidx.annotation:annotation:1.1.0")
-        api(project(':compose:animation:animation'))
+        api("androidx.compose.animation:animation:1.0.0")
         api(project(':compose:runtime:runtime'))
-        api(project(':compose:ui:ui'))
+        api("androidx.compose.ui:ui:1.0.0")
 
         implementation(libs.kotlinStdlibCommon)
         implementation(project(":compose:foundation:foundation-layout"))
-        implementation(project(":compose:ui:ui-text"))
-        implementation(project(":compose:ui:ui-util"))
+        implementation(project(":compose:ui:ui-graphics"))
+        implementation("androidx.compose.ui:ui-text:1.0.0")
+        implementation("androidx.compose.ui:ui-util:1.0.0")
 
         testImplementation(project(":compose:test-utils"))
         testImplementation(libs.testRules)
diff --git a/compose/foundation/foundation/samples/build.gradle b/compose/foundation/foundation/samples/build.gradle
index 6a77d10..3ff4f3f 100644
--- a/compose/foundation/foundation/samples/build.gradle
+++ b/compose/foundation/foundation/samples/build.gradle
@@ -31,13 +31,13 @@
 
     compileOnly(project(":annotation:annotation-sampled"))
 
-    implementation(project(":compose:animation:animation"))
+    implementation("androidx.compose.animation:animation:1.0.0")
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:foundation:foundation-layout"))
-    implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material:1.0.0")
     implementation(project(":compose:runtime:runtime"))
-    implementation(project(":compose:ui:ui"))
-    implementation(project(":compose:ui:ui-text"))
+    implementation("androidx.compose.ui:ui:1.0.0")
+    implementation("androidx.compose.ui:ui-text:1.0.0")
 }
 
 androidx {
diff --git a/compose/integration-tests/docs-snippets/build.gradle b/compose/integration-tests/docs-snippets/build.gradle
index f2350fd..dd7311c 100644
--- a/compose/integration-tests/docs-snippets/build.gradle
+++ b/compose/integration-tests/docs-snippets/build.gradle
@@ -29,6 +29,7 @@
     implementation("androidx.recyclerview:recyclerview:1.2.1")
 
     kotlinPlugin(project(":compose:compiler:compiler"))
+    implementation(project(":compose:animation:animation-graphics"))
     implementation(project(":compose:foundation:foundation-layout"))
     implementation(project(":compose:material:material"))
     implementation(project(":compose:material:material-icons-extended"))
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
index 9c0d71e..c66d0c3 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
@@ -23,6 +23,8 @@
 
 package androidx.compose.integration.docs.resources
 
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.Divider
 import androidx.compose.material.Icon
@@ -32,6 +34,9 @@
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.rounded.Menu
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.colorResource
@@ -124,6 +129,21 @@
     )
 }
 
+@OptIn(ExperimentalAnimationGraphicsApi::class)
+@Composable
+private fun ResourcesSnippet7() {
+    // Files in res/drawable folders. For example:
+    // - res/drawable/animated_vector.xml
+
+    // In your Compose code
+    val image = animatedVectorResource(id = R.drawable.animated_vector)
+    val atEnd by remember { mutableStateOf(false) }
+    Icon(
+        painter = image.painterFor(atEnd = atEnd),
+        contentDescription = null // decorative element
+    )
+}
+
 @Composable
 private fun ResourcesSnippet8() {
     Icon(Icons.Rounded.Menu, contentDescription = "Localized description")
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
index 4ba6f05..e9b517e 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/testing/Testing.kt
@@ -87,7 +87,7 @@
 }
 
 private object TestingSnippet3 {
-    // file: app/src/androidTest/java/com/package/MyComposeTest.kt
+    // file: app/src/androidTest/kotlin/com/package/MyComposeTest.kt
 
     class MyComposeTest {
 
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index 9a4a588..10bac0a 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -206,6 +206,8 @@
 
   @androidx.compose.runtime.Immutable public abstract sealed class Brush {
     method public abstract void applyTo(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    method public long getIntrinsicSize();
+    property public long intrinsicSize;
     field public static final androidx.compose.ui.graphics.Brush.Companion Companion;
   }
 
@@ -417,6 +419,7 @@
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
     method public android.graphics.Shader createShader(long size);
+    property public long intrinsicSize;
   }
 
   public final inline class Matrix {
@@ -646,6 +649,7 @@
 
   @androidx.compose.runtime.Immutable public final class RadialGradient extends androidx.compose.ui.graphics.ShaderBrush {
     method public android.graphics.Shader createShader(long size);
+    property public long intrinsicSize;
   }
 
   public final class RectHelper_androidKt {
@@ -1177,6 +1181,15 @@
     method public static androidx.compose.ui.graphics.painter.BitmapPainter BitmapPainter(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional int filterQuality);
   }
 
+  public final class BrushPainter extends androidx.compose.ui.graphics.painter.Painter {
+    ctor public BrushPainter(androidx.compose.ui.graphics.Brush brush);
+    method public androidx.compose.ui.graphics.Brush getBrush();
+    method public long getIntrinsicSize();
+    method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+    property public final androidx.compose.ui.graphics.Brush brush;
+    property public long intrinsicSize;
+  }
+
   public final class ColorPainter extends androidx.compose.ui.graphics.painter.Painter {
     ctor public ColorPainter(long color);
     method public long getColor();
diff --git a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
index 83d79a0..0aa058c 100644
--- a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -206,6 +206,8 @@
 
   @androidx.compose.runtime.Immutable public abstract sealed class Brush {
     method public abstract void applyTo(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    method public long getIntrinsicSize();
+    property public long intrinsicSize;
     field public static final androidx.compose.ui.graphics.Brush.Companion Companion;
   }
 
@@ -422,6 +424,7 @@
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
     method public android.graphics.Shader createShader(long size);
+    property public long intrinsicSize;
   }
 
   public final inline class Matrix {
@@ -651,6 +654,7 @@
 
   @androidx.compose.runtime.Immutable public final class RadialGradient extends androidx.compose.ui.graphics.ShaderBrush {
     method public android.graphics.Shader createShader(long size);
+    property public long intrinsicSize;
   }
 
   public final class RectHelper_androidKt {
@@ -1182,6 +1186,15 @@
     method public static androidx.compose.ui.graphics.painter.BitmapPainter BitmapPainter(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional int filterQuality);
   }
 
+  public final class BrushPainter extends androidx.compose.ui.graphics.painter.Painter {
+    ctor public BrushPainter(androidx.compose.ui.graphics.Brush brush);
+    method public androidx.compose.ui.graphics.Brush getBrush();
+    method public long getIntrinsicSize();
+    method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+    property public final androidx.compose.ui.graphics.Brush brush;
+    property public long intrinsicSize;
+  }
+
   public final class ColorPainter extends androidx.compose.ui.graphics.painter.Painter {
     ctor public ColorPainter(long color);
     method public long getColor();
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index b0a82ea..6f43701 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -236,6 +236,8 @@
 
   @androidx.compose.runtime.Immutable public abstract sealed class Brush {
     method public abstract void applyTo(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+    method public long getIntrinsicSize();
+    property public long intrinsicSize;
     field public static final androidx.compose.ui.graphics.Brush.Companion Companion;
   }
 
@@ -449,6 +451,7 @@
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
     method public android.graphics.Shader createShader(long size);
+    property public long intrinsicSize;
   }
 
   public final inline class Matrix {
@@ -678,6 +681,7 @@
 
   @androidx.compose.runtime.Immutable public final class RadialGradient extends androidx.compose.ui.graphics.ShaderBrush {
     method public android.graphics.Shader createShader(long size);
+    property public long intrinsicSize;
   }
 
   public final class RectHelper_androidKt {
@@ -1233,6 +1237,15 @@
     method public static androidx.compose.ui.graphics.painter.BitmapPainter BitmapPainter(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional int filterQuality);
   }
 
+  public final class BrushPainter extends androidx.compose.ui.graphics.painter.Painter {
+    ctor public BrushPainter(androidx.compose.ui.graphics.Brush brush);
+    method public androidx.compose.ui.graphics.Brush getBrush();
+    method public long getIntrinsicSize();
+    method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+    property public final androidx.compose.ui.graphics.Brush brush;
+    property public long intrinsicSize;
+  }
+
   public final class ColorPainter extends androidx.compose.ui.graphics.painter.Painter {
     ctor public ColorPainter(long color);
     method public long getColor();
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
index dd24cefd..786ed1f 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ShaderTest.kt
@@ -120,6 +120,83 @@
         assertEquals(Color.Blue, pixelMap[5, centerY - 5])
     }
 
+    @Test
+    fun testLinearGradientIntrinsicSize() {
+        assertEquals(
+            Size(100f, 200f),
+            Brush.linearGradient(
+                listOf(Color.Red, Color.Blue),
+                start = Offset(200f, 100f),
+                end = Offset(300f, 300f)
+            ).intrinsicSize
+        )
+    }
+
+    @Test
+    fun testLinearGradientNegativePosition() {
+        assertEquals(
+            Size(100f, 200f),
+            Brush.linearGradient(
+                listOf(Color.Red, Color.Blue),
+                start = Offset(200f, 100f),
+                end = Offset(100f, -100f)
+            ).intrinsicSize
+        )
+    }
+
+    @Test
+    fun testLinearGradientInfiniteWidth() {
+        assertEquals(
+            Size(Float.NaN, 200f),
+            Brush.linearGradient(
+                listOf(Color.Red, Color.Blue),
+                start = Offset(Float.POSITIVE_INFINITY, 100f),
+                end = Offset(Float.POSITIVE_INFINITY, 300f)
+            ).intrinsicSize
+        )
+    }
+
+    @Test
+    fun testLinearGradientInfiniteHeight() {
+        assertEquals(
+            Size(100f, Float.NaN),
+            Brush.linearGradient(
+                listOf(Color.Red, Color.Blue),
+                start = Offset(100f, 0f),
+                end = Offset(200f, Float.POSITIVE_INFINITY)
+            ).intrinsicSize
+        )
+    }
+
+    @Test
+    fun testSweepGradientIntrinsicSize() {
+        // Sweep gradients do not have an intrinsic size as they sweep/fill the geometry they are
+        // drawn with
+        assertEquals(
+            Size.Unspecified,
+            Brush.sweepGradient(listOf(Color.Red, Color.Blue)).intrinsicSize
+        )
+    }
+
+    @Test
+    fun testRadialGradientIntrinsicSize() {
+        assertEquals(
+            Size(100f, 100f),
+            Brush.radialGradient(
+                listOf(Color.Red, Color.Blue),
+                radius = 50f
+            ).intrinsicSize
+        )
+    }
+
+    @Test
+    fun testRadialGradientInfiniteSize() {
+        assertEquals(
+            Size.Unspecified,
+            Brush.radialGradient(listOf(Color.Red, Color.Blue)).intrinsicSize
+        )
+    }
+
     private fun ImageBitmap.drawInto(
         block: DrawScope.() -> Unit
     ) = CanvasDrawScope().draw(
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/BrushPainterTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/BrushPainterTest.kt
new file mode 100644
index 0000000..aed63bc
--- /dev/null
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/BrushPainterTest.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics.painter
+
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.BlendMode
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.Paint
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.graphics.toPixelMap
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BrushPainterTest {
+
+    private fun createImageBitmap(): ImageBitmap {
+        val image = ImageBitmap(100, 100)
+        Canvas(image).drawRect(
+            0f,
+            0f,
+            100f,
+            100f,
+            Paint().apply { color = Color.White }
+        )
+        return image
+    }
+
+    @Test
+    fun testBrushPainter() {
+        val brushPainter = BrushPainter(
+            Brush.verticalGradient(
+                0.0f to Color.Red,
+                0.5f to Color.Red,
+                0.5f to Color.Blue,
+                1.0f to Color.Blue
+            )
+        )
+        val image = createImageBitmap()
+        drawPainter(brushPainter, Canvas(image), Size(100f, 100f))
+        val pixelMap = image.toPixelMap()
+        assertEquals(Color.Red, pixelMap[0, 0])
+        assertEquals(Color.Red, pixelMap[99, 0])
+        assertEquals(Color.Red, pixelMap[0, 49])
+        assertEquals(Color.Red, pixelMap[99, 49])
+
+        assertEquals(Color.Blue, pixelMap[0, 50])
+        assertEquals(Color.Blue, pixelMap[99, 50])
+        assertEquals(Color.Blue, pixelMap[0, 99])
+        assertEquals(Color.Blue, pixelMap[99, 99])
+    }
+
+    @Test
+    fun testBrushPainterAlphaApplied() {
+        val brushPainter = BrushPainter(
+            Brush.verticalGradient(
+                0.0f to Color.Red,
+                0.5f to Color.Red,
+                0.5f to Color.Blue,
+                1.0f to Color.Blue
+            )
+        )
+        val image = createImageBitmap()
+        drawPainter(brushPainter, Canvas(image), Size(100f, 100f), alpha = 0.5f)
+
+        val expectedRed = Color(
+            alpha = 0.5f,
+            red = Color.Red.red,
+            green = 0f,
+            blue = 0f
+        ).compositeOver(Color.White)
+
+        val expectedBlue = Color(
+            alpha = 0.5f,
+            red = 0f,
+            green = 0f,
+            blue = Color.Blue.blue
+        ).compositeOver(Color.White)
+
+        val pixelMap = image.toPixelMap()
+        assertEquals(expectedRed, pixelMap[0, 0])
+        assertEquals(expectedRed, pixelMap[99, 0])
+        assertEquals(expectedRed, pixelMap[0, 49])
+        assertEquals(expectedRed, pixelMap[99, 49])
+
+        assertEquals(expectedBlue, pixelMap[0, 50])
+        assertEquals(expectedBlue, pixelMap[99, 50])
+        assertEquals(expectedBlue, pixelMap[0, 99])
+        assertEquals(expectedBlue, pixelMap[99, 99])
+    }
+
+    @Test
+    fun testBrushPainterTint() {
+        val brushPainter = BrushPainter(
+            Brush.verticalGradient(
+                0.0f to Color.Red,
+                0.5f to Color.Red,
+                0.5f to Color.Blue,
+                1.0f to Color.Blue
+            )
+        )
+        val image = createImageBitmap()
+        drawPainter(
+            brushPainter,
+            Canvas(image),
+            Size(100f, 100f),
+            colorFilter = ColorFilter.tint(Color.Cyan, BlendMode.SrcIn)
+        )
+        val pixelMap = image.toPixelMap()
+        assertEquals(Color.Cyan, pixelMap[0, 0])
+        assertEquals(Color.Cyan, pixelMap[99, 0])
+        assertEquals(Color.Cyan, pixelMap[0, 49])
+        assertEquals(Color.Cyan, pixelMap[99, 49])
+
+        assertEquals(Color.Cyan, pixelMap[0, 50])
+        assertEquals(Color.Cyan, pixelMap[99, 50])
+        assertEquals(Color.Cyan, pixelMap[0, 99])
+        assertEquals(Color.Cyan, pixelMap[99, 99])
+    }
+
+    @Test
+    fun testBrushPainterEquals() {
+        val brush1 = Brush.verticalGradient(
+            0.0f to Color.Red,
+            0.3f to Color.Blue,
+            0.7f to Color.Green
+        )
+
+        val brush2 = Brush.verticalGradient(
+            0.0f to Color.Red,
+            0.3f to Color.Blue,
+            0.7f to Color.Green
+        )
+
+        assertEquals(BrushPainter(brush1), BrushPainter(brush2))
+    }
+
+    @Test
+    fun testBrushPainterHashCode() {
+        val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue, Color.Yellow))
+        assertEquals(BrushPainter(brush).hashCode(), brush.hashCode())
+    }
+
+    @Test
+    fun testBrushPainterToString() {
+        val brush = Brush.verticalGradient(
+            listOf(Color.White, Color.Black, Color.Gray, Color.LightGray)
+        )
+        assertEquals("BrushPainter(brush=$brush)", BrushPainter(brush).toString())
+    }
+
+    @Test
+    fun testBrushPainterIntrinsicSize() {
+        val brush = Brush.verticalGradient(
+            listOf(Color.White, Color.Black),
+            startY = 0f,
+            endY = 100f
+        )
+        assertEquals(Size(0.0f, 100f), BrushPainter(brush).intrinsicSize)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
index 35d0218..bda9615 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt
@@ -24,10 +24,20 @@
 import androidx.compose.ui.geometry.isFinite
 import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.geometry.isUnspecified
-import androidx.compose.ui.unit.center
+import kotlin.math.abs
 
 @Immutable
 sealed class Brush {
+
+    /**
+     * Return the intrinsic size of the [Brush].
+     * If the there is no intrinsic size (i.e. filling bounds with an arbitrary color) return
+     * [Size.Unspecified].
+     * If there is no intrinsic size in a single dimension, return [Size] with
+     * [Float.NaN] in the desired dimension.
+     */
+    open val intrinsicSize: Size = Size.Unspecified
+
     abstract fun applyTo(size: Size, p: Paint, alpha: Float)
 
     companion object {
@@ -434,6 +444,13 @@
     private val tileMode: TileMode = TileMode.Clamp
 ) : ShaderBrush() {
 
+    override val intrinsicSize: Size
+        get() =
+            Size(
+                if (start.x.isFinite() && end.x.isFinite()) abs(start.x - end.x) else Float.NaN,
+                if (start.y.isFinite() && end.y.isFinite()) abs(start.y - end.y) else Float.NaN
+            )
+
     override fun createShader(size: Size): Shader {
         val startX = if (start.x == Float.POSITIVE_INFINITY) size.width else start.x
         val startY = if (start.y == Float.POSITIVE_INFINITY) size.height else start.y
@@ -493,6 +510,9 @@
     private val tileMode: TileMode = TileMode.Clamp
 ) : ShaderBrush() {
 
+    override val intrinsicSize: Size
+        get() = if (radius.isFinite()) Size(radius * 2, radius * 2) else Size.Unspecified
+
     override fun createShader(size: Size): Shader {
         val centerX: Float
         val centerY: Float
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
index cf3527d..85217ee 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
@@ -112,6 +112,11 @@
     colorStops: List<Float>?,
 ): Shader
 
+/**
+ * Creates a Shader using the given [ImageBitmap] as an input texture. If the shader is
+ * to be drawn in an area larger than the size of the [ImageBitmap], the region is filled
+ * in the horizontal and vertical directions based on the [tileModeX] and [tileModeY] parameters.
+ */
 fun ImageShader(
     image: ImageBitmap,
     tileModeX: TileMode = TileMode.Clamp,
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/BrushPainter.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/BrushPainter.kt
new file mode 100644
index 0000000..cfd8419
--- /dev/null
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/BrushPainter.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics.painter
+
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.drawscope.DrawScope
+
+/**
+ * [Painter] implementation used to fill the provided bounds with the specified [Brush].
+ * The intrinsic size of this [Painter] is determined by [Brush.intrinsicSize]
+ */
+class BrushPainter(
+    val brush: Brush,
+) : Painter() {
+
+    private var alpha: Float = 1.0f
+    private var colorFilter: ColorFilter? = null
+
+    override val intrinsicSize: Size
+        get() = brush.intrinsicSize
+
+    override fun DrawScope.onDraw() {
+        drawRect(brush = brush, alpha = alpha, colorFilter = colorFilter)
+    }
+
+    override fun applyAlpha(alpha: Float): Boolean {
+        this.alpha = alpha
+        return true
+    }
+
+    override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
+        this.colorFilter = colorFilter
+        return true
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is BrushPainter) return false
+
+        if (brush != other.brush) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return brush.hashCode()
+    }
+
+    override fun toString(): String {
+        return "BrushPainter(brush=$brush)"
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/Painter.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/Painter.kt
index 0149166..cad2d23 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/Painter.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/Painter.kt
@@ -152,7 +152,7 @@
      * If the there is no intrinsic size (i.e. filling bounds with an arbitrary color) return
      * [Size.Unspecified].
      * If there is no intrinsic size in a single dimension, return [Size] with
-     * [Float.POSITIVE_INFINITY] in the desired dimension.
+     * [Float.NaN] in the desired dimension.
      * If a [Painter] does not have an intrinsic size, it will always draw within the full
      * bounds of the destination
      */
diff --git a/compose/ui/ui-inspection/build.gradle b/compose/ui/ui-inspection/build.gradle
index 014b40f..8b917d1 100644
--- a/compose/ui/ui-inspection/build.gradle
+++ b/compose/ui/ui-inspection/build.gradle
@@ -48,6 +48,7 @@
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
     androidTestImplementation(project(":compose:ui:ui-tooling-data"))
     androidTestImplementation(project(":compose:ui:ui"))
+    androidTestImplementation(project(":compose:ui:ui-util"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
     androidTestImplementation(project(":compose:material:material"))
     androidTestImplementation(project(":inspection:inspection-testing"))
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/ParameterFactoryTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/ParameterFactoryTest.kt
index 91087fb..e60d546 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/ParameterFactoryTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/ParameterFactoryTest.kt
@@ -69,6 +69,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.em
 import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.packFloats
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -218,12 +219,21 @@
                     parameter("x", ParameterType.DimensionDp, 2.5f)
                     parameter("y", ParameterType.DimensionDp, 5.0f)
                 }
+                parameter("intrinsicSize", ParameterType.String, Size::class.java.simpleName) {
+                    val width = 5.0f
+                    val height = 9.5f
+                    parameter("height", ParameterType.Float, height)
+                    parameter("maxDimension", ParameterType.Float, height)
+                    parameter("minDimension", ParameterType.Float, width)
+                    parameter("packedValue", ParameterType.Int64, packFloats(width, height))
+                    parameter("width", ParameterType.Float, width)
+                }
                 parameter("start", ParameterType.String, Offset::class.java.simpleName) {
                     parameter("x", ParameterType.DimensionDp, 0.0f)
                     parameter("y", ParameterType.DimensionDp, 0.25f)
                 }
-                parameter("tileMode", ParameterType.String, "Clamp", index = 4)
-                parameter("createdSize", ParameterType.String, "Unspecified", index = 5)
+                parameter("tileMode", ParameterType.String, "Clamp", index = 5)
+                parameter("createdSize", ParameterType.String, "Unspecified", index = 6)
             }
         }
         // TODO: add tests for RadialGradient & ShaderBrush
diff --git a/compose/ui/ui-tooling-preview/build.gradle b/compose/ui/ui-tooling-preview/build.gradle
index 201c607..495790ae 100644
--- a/compose/ui/ui-tooling-preview/build.gradle
+++ b/compose/ui/ui-tooling-preview/build.gradle
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+
+import androidx.build.AndroidXComposePlugin
 import androidx.build.LibraryGroups
 import androidx.build.LibraryType
 
@@ -21,17 +23,43 @@
     id("AndroidXPlugin")
     id("AndroidXComposePlugin")
     id("com.android.library")
-    id("org.jetbrains.kotlin.android")
 }
 
+AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
+
+
 dependencies {
-    implementation(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.2.0")
-    api(project(":compose:runtime:runtime"))
-
-    testImplementation(libs.junit)
+    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
+        implementation(libs.kotlinStdlib)
+        api("androidx.annotation:annotation:1.2.0")
+        api(project(":compose:runtime:runtime"))
+        testImplementation(libs.junit)
+    }
 }
 
+if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
+    kotlin {
+        android()
+        jvm("desktop")
+
+        /*
+         * When updating dependencies, make sure to make the an an analogous update in the
+         * corresponding block above
+         */
+        sourceSets {
+            commonMain.dependencies {
+                implementation(libs.kotlinStdlibCommon)
+                api("androidx.annotation:annotation:1.2.0")
+                api(project(":compose:runtime:runtime"))
+            }
+
+            androidMain.dependsOn(jvmMain)
+            desktopMain.dependsOn(jvmMain)
+        }
+    }
+}
+
+
 androidx {
     name = "Compose Tooling API"
     type = LibraryType.PUBLISHED_LIBRARY
diff --git a/compose/ui/ui-tooling-preview/src/main/AndroidManifest.xml b/compose/ui/ui-tooling-preview/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from compose/ui/ui-tooling-preview/src/main/AndroidManifest.xml
rename to compose/ui/ui-tooling-preview/src/androidMain/AndroidManifest.xml
diff --git a/compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/Device.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
similarity index 100%
rename from compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/Device.kt
rename to compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Device.kt
diff --git a/compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/Preview.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Preview.kt
similarity index 100%
rename from compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/Preview.kt
rename to compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/Preview.kt
diff --git a/compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/PreviewParameter.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/PreviewParameter.kt
similarity index 100%
rename from compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/PreviewParameter.kt
rename to compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/PreviewParameter.kt
diff --git a/compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/UiMode.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/UiMode.kt
similarity index 100%
rename from compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/UiMode.kt
rename to compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/UiMode.kt
diff --git a/compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/datasource/CollectionPreviewParameterProvider.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/CollectionPreviewParameterProvider.kt
similarity index 100%
rename from compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/datasource/CollectionPreviewParameterProvider.kt
rename to compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/CollectionPreviewParameterProvider.kt
diff --git a/compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt
similarity index 100%
rename from compose/ui/ui-tooling-preview/src/main/java/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt
rename to compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt
diff --git a/compose/ui/ui-tooling/src/desktopMain/kotlin/androidx/compose/desktop/ui/tooling/preview/Preview.kt b/compose/ui/ui-tooling-preview/src/desktopMain/androidx/compose/desktop/ui/tooling/preview/Preview.kt
similarity index 100%
rename from compose/ui/ui-tooling/src/desktopMain/kotlin/androidx/compose/desktop/ui/tooling/preview/Preview.kt
rename to compose/ui/ui-tooling-preview/src/desktopMain/androidx/compose/desktop/ui/tooling/preview/Preview.kt
diff --git a/compose/ui/ui-tooling/build.gradle b/compose/ui/ui-tooling/build.gradle
index ff3f55b..a9e348e 100644
--- a/compose/ui/ui-tooling/build.gradle
+++ b/compose/ui/ui-tooling/build.gradle
@@ -71,14 +71,13 @@
         sourceSets {
             commonMain.dependencies {
                 implementation(libs.kotlinStdlibCommon)
-
+                api(project(":compose:ui:ui-tooling-preview"))
+                api(project(":compose:runtime:runtime"))
+                api(project(":compose:ui:ui"))
             }
             androidMain.dependencies {
                 api("androidx.annotation:annotation:1.1.0")
 
-                api(project(":compose:runtime:runtime"))
-                api(project(":compose:ui:ui"))
-                api(project(":compose:ui:ui-tooling-preview"))
                 api(project(":compose:ui:ui-tooling-data"))
                 implementation(project(":compose:material:material"))
                 implementation("androidx.activity:activity-compose:1.3.0")
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
index 6358a0b..085a1f7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Stable
@@ -288,6 +289,36 @@
     }
 
     @Test
+    fun replacedChildren_includeFakeNodes() {
+        val tag = "tag1"
+        rule.setContent {
+            SimpleTestLayout(Modifier.clickable(role = Role.Button, onClick = {}).testTag(tag)) {
+                BasicText("text")
+            }
+        }
+
+        val node = rule.onNodeWithTag(tag, true).fetchSemanticsNode()
+        val children = node.replacedChildren
+        assertThat(children.count()).isEqualTo(2)
+        assertThat(children.last().isFake).isTrue()
+    }
+
+    @Test
+    fun children_doNotIncludeFakeNodes() {
+        val tag = "tag1"
+        rule.setContent {
+            SimpleTestLayout(Modifier.clickable(role = Role.Button, onClick = {}).testTag(tag)) {
+                BasicText("text")
+            }
+        }
+
+        val node = rule.onNodeWithTag(tag, true).fetchSemanticsNode()
+        val children = node.children
+        assertThat(children.count()).isEqualTo(1)
+        assertThat(children.last().isFake).isFalse()
+    }
+
+    @Test
     fun removingMergedSubtree_updatesSemantics() {
         val label = "foo"
         val showSubtree = mutableStateOf(true)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
index 328b44f..41b1f8b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
@@ -161,7 +161,7 @@
             unmergedChildren().fastForEach { child ->
                 // Don't merge children that themselves merge all their descendants (because that
                 // indicates they're independently screen-reader-focusable).
-                if (!child.isFake && !child.isMergingSemanticsOfDescendants) {
+                if (!child.isMergingSemanticsOfDescendants) {
                     mergedConfig.mergeChild(child.unmergedConfig)
                     child.mergeConfig(mergedConfig)
                 }
@@ -172,7 +172,10 @@
     private val isMergingSemanticsOfDescendants: Boolean
         get() = mergingEnabled && unmergedConfig.isMergingSemanticsOfDescendants
 
-    internal fun unmergedChildren(sortByBounds: Boolean = false): List<SemanticsNode> {
+    internal fun unmergedChildren(
+        sortByBounds: Boolean = false,
+        includeFakeNodes: Boolean = false
+    ): List<SemanticsNode> {
         if (this.isFake) return listOf()
         val unmergedChildren: MutableList<SemanticsNode> = mutableListOf()
 
@@ -185,7 +188,9 @@
             unmergedChildren.add(SemanticsNode(semanticsChild, mergingEnabled))
         }
 
-        emitFakeNodes(unmergedChildren)
+        if (includeFakeNodes) {
+            emitFakeNodes(unmergedChildren)
+        }
 
         return unmergedChildren
     }
@@ -199,7 +204,11 @@
     // TODO(b/184376083): This is too expensive for a val (full subtree recreation every call);
     //               optimize this when the merging algorithm is improved.
     val children: List<SemanticsNode>
-        get() = getChildren(sortByBounds = false, includeReplacedSemantics = !mergingEnabled)
+        get() = getChildren(
+            sortByBounds = false,
+            includeReplacedSemantics = !mergingEnabled,
+            includeFakeNodes = false
+        )
 
     /**
      * Contains the children in inverse hit test order (i.e. paint order).
@@ -209,7 +218,11 @@
      * This property is primarily used in Accessibility delegate.
      */
     internal val replacedChildren: List<SemanticsNode>
-        get() = getChildren(sortByBounds = false, includeReplacedSemantics = false)
+        get() = getChildren(
+            sortByBounds = false,
+            includeReplacedSemantics = false,
+            includeFakeNodes = true
+        )
 
     /**
      * Similar to [replacedChildren] but children are sorted by bounds: top to down, left to
@@ -218,11 +231,27 @@
     // TODO(b/184376083): This is too expensive for a val (full subtree recreation every call);
     //               optimize this when the merging algorithm is improved.
     internal val replacedChildrenSortedByBounds: List<SemanticsNode>
-        get() = getChildren(sortByBounds = true, includeReplacedSemantics = false)
+        get() = getChildren(
+            sortByBounds = true,
+            includeReplacedSemantics = false,
+            includeFakeNodes = true
+        )
 
+    /**
+     * @param sortByBounds if true, nodes in the result list will be sorted with respect to their
+     * bounds. Otherwise children will be in the order they are added to the composition
+     * @param includeReplacedSemantics if true, the result will contain children of nodes marked
+     * as [clearAndSetSemantics]. For accessibility we always use false, but in testing and
+     * debugging we should be able to investigate both
+     * @param includeFakeNodes if true, the tree will include fake nodes. For accessibility we
+     * set to true, but for testing purposes we don't want to expose the fake nodes and therefore
+     * set to false. When Talkback can properly handle unmerged tree, fake nodes will be removed
+     * and so will be this parameter.
+     */
     private fun getChildren(
         sortByBounds: Boolean,
-        includeReplacedSemantics: Boolean
+        includeReplacedSemantics: Boolean,
+        includeFakeNodes: Boolean
     ): List<SemanticsNode> {
         if (!includeReplacedSemantics && unmergedConfig.isClearingSemantics) {
             return listOf()
@@ -235,7 +264,7 @@
             return findOneLayerOfMergingSemanticsNodes(sortByBounds = sortByBounds)
         }
 
-        return unmergedChildren(sortByBounds)
+        return unmergedChildren(sortByBounds, includeFakeNodes)
     }
 
     /**
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.desktop.kt
index f27160b..ac41d30 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.desktop.kt
@@ -148,13 +148,17 @@
             return list.lastOrNull()
         }
 
+    private fun DesktopOwner?.isAbove(
+        targetOwner: DesktopOwner?
+    ) = list.indexOf(this) > list.indexOf(targetOwner)
+
     fun onMousePressed(x: Int, y: Int, nativeEvent: MouseEvent? = null) {
         isMousePressed = true
         val currentOwner = hoveredOwner
         if (currentOwner != null) {
-            if (currentOwner.isFocusable && focusedOwner != currentOwner) {
+            if (focusedOwner.isAbove(currentOwner)) {
                 focusedOwner?.onDismissRequest?.invoke()
-                focusedOwner = currentOwner
+                return
             } else {
                 currentOwner.processPointerInput(
                     pointerInputEvent(nativeEvent, x, y, isMousePressed)
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
index 97b77a7..63d6a76 100644
--- a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
@@ -79,7 +79,6 @@
                 .setShortLabel("short label")
                 .setLongLabel("long label")
                 .setIntent(intent)
-                .setIcon(IconCompat.createWithContentUri("content://abc"))
                 .build();
 
         mShortcutInfoChangeListener.onShortcutUpdated(Collections.singletonList(shortcut));
@@ -221,7 +220,6 @@
                 .setShortLabel("short label")
                 .setLongLabel("long label")
                 .setIntent(intent)
-                .setIcon(IconCompat.createWithContentUri("content://abc"))
                 .build();
 
         mShortcutInfoChangeListener.onShortcutAdded(Collections.singletonList(shortcut));
@@ -284,4 +282,58 @@
         assertThat(actionsString).containsExactly(expectedAction1.toString(),
                 expectedAction2.toString());
     }
+
+    @Test
+    @SmallTest
+    public void onShortcutUpdated_withUriIcon_savesToAppIndexWithUri() throws Exception {
+        ArgumentCaptor<Indexable> indexableCaptor = ArgumentCaptor.forClass(Indexable.class);
+
+        Intent intent = Intent.parseUri("app://shortcut", 0);
+        ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(mContext, "publicIntent")
+                .setShortLabel("short label")
+                .setIntent(intent)
+                .setIcon(IconCompat.createWithContentUri(
+                        "file:///data/user/0/com.example.myapp/files/ic_myicon.jpg"))
+                .build();
+
+        mShortcutInfoChangeListener.onShortcutUpdated(Collections.singletonList(shortcut));
+
+        verify(mFirebaseAppIndex, only()).update(indexableCaptor.capture());
+        List<Indexable> allValues = indexableCaptor.getAllValues();
+        Indexable expected = new ShortcutBuilder()
+                .setId("publicIntent")
+                .setShortcutLabel("short label")
+                .setUrl(ShortcutUtils.getIndexableUrl(mContext, "publicIntent"))
+                .setShortcutUrl(ShortcutUtils.getIndexableShortcutUrl(mContext, intent, null))
+                .setImage("file:///data/user/0/com.example.myapp/files/ic_myicon.jpg")
+                .build();
+        assertThat(allValues).containsExactly(expected);
+    }
+
+    @Test
+    @SmallTest
+    public void onShortcutUpdated_withAdaptiveUriIcon_savesToAppIndexWithUri() throws Exception {
+        ArgumentCaptor<Indexable> indexableCaptor = ArgumentCaptor.forClass(Indexable.class);
+
+        Intent intent = Intent.parseUri("app://shortcut", 0);
+        ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(mContext, "publicIntent")
+                .setShortLabel("short label")
+                .setIntent(intent)
+                .setIcon(IconCompat.createWithAdaptiveBitmapContentUri(
+                        "file:///data/user/0/com.example.myapp/files/ic_myicon.jpg"))
+                .build();
+
+        mShortcutInfoChangeListener.onShortcutUpdated(Collections.singletonList(shortcut));
+
+        verify(mFirebaseAppIndex, only()).update(indexableCaptor.capture());
+        List<Indexable> allValues = indexableCaptor.getAllValues();
+        Indexable expected = new ShortcutBuilder()
+                .setId("publicIntent")
+                .setShortcutLabel("short label")
+                .setUrl(ShortcutUtils.getIndexableUrl(mContext, "publicIntent"))
+                .setShortcutUrl(ShortcutUtils.getIndexableShortcutUrl(mContext, intent, null))
+                .setImage("file:///data/user/0/com.example.myapp/files/ic_myicon.jpg")
+                .build();
+        assertThat(allValues).containsExactly(expected);
+    }
 }
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
index 88e9a59..5c5afd7 100644
--- a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
@@ -34,6 +34,7 @@
 import androidx.core.google.shortcuts.builders.CapabilityBuilder;
 import androidx.core.google.shortcuts.builders.ParameterBuilder;
 import androidx.core.google.shortcuts.builders.ShortcutBuilder;
+import androidx.core.graphics.drawable.IconCompat;
 
 import com.google.crypto.tink.KeysetHandle;
 import com.google.firebase.appindexing.Action;
@@ -191,6 +192,16 @@
             }
         }
 
+        // Add icon
+        if (shortcut.getIcon() != null) {
+            IconCompat icon = shortcut.getIcon();
+            if (icon.getType() == IconCompat.TYPE_URI_ADAPTIVE_BITMAP
+                    || icon.getType() == IconCompat.TYPE_URI) {
+                // Assume the uri is public and can be opened by Google apps
+                shortcutBuilder.setImage(icon.getUri().toString());
+            }
+        }
+
         // By default, the indexable will be saved only on-device.
         return shortcutBuilder.build();
     }
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 2baef92..b6ca37c 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -256,6 +256,7 @@
     docs(project(":wear:compose:compose-foundation"))
     samples(project(":wear:compose:compose-foundation-samples"))
     docs(project(":wear:compose:compose-material"))
+    samples(project(":wear:compose:compose-material-samples"))
     docs(project(":wear:wear-input"))
     docs(project(":wear:wear-input-testing"))
     docs(project(":wear:wear-ongoing"))
diff --git a/hilt/hilt-navigation-compose/build.gradle b/hilt/hilt-navigation-compose/build.gradle
index 1989f53..078d991 100644
--- a/hilt/hilt-navigation-compose/build.gradle
+++ b/hilt/hilt-navigation-compose/build.gradle
@@ -35,12 +35,12 @@
 }
 
 dependencies {
-    kotlinPlugin(project(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
 
     implementation(libs.kotlinStdlib)
     api("androidx.hilt:hilt-navigation:1.0.0")
-    api(project(":compose:runtime:runtime"))
-    api("androidx.compose.ui:ui:1.0.0-rc02")
+    api("androidx.compose.runtime:runtime:1.0.1")
+    api("androidx.compose.ui:ui:1.0.1")
     api("androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07")
     api(projectOrArtifact(":navigation:navigation-compose"))
 
diff --git a/hilt/hilt-navigation-compose/samples/build.gradle b/hilt/hilt-navigation-compose/samples/build.gradle
index 3c00b2c..8eec1e3 100644
--- a/hilt/hilt-navigation-compose/samples/build.gradle
+++ b/hilt/hilt-navigation-compose/samples/build.gradle
@@ -27,7 +27,7 @@
 }
 
 dependencies {
-    kotlinPlugin(project(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
     implementation(libs.kotlinStdlib)
 
     compileOnly(projectOrArtifact(":annotation:annotation-sampled"))
diff --git a/lifecycle/lifecycle-viewmodel-compose/build.gradle b/lifecycle/lifecycle-viewmodel-compose/build.gradle
index 6bf586b..5346124 100644
--- a/lifecycle/lifecycle-viewmodel-compose/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/build.gradle
@@ -27,11 +27,11 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
 
     api "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0"
-    api(projectOrArtifact(":compose:runtime:runtime"))
-    api "androidx.compose.ui:ui:1.0.0-rc02"
+    api("androidx.compose.runtime:runtime:1.0.1")
+    api "androidx.compose.ui:ui:1.0.1"
 
     implementation(libs.kotlinStdlib)
 
diff --git a/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle b/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
index f4fcc19..70c9431 100644
--- a/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
@@ -26,7 +26,7 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
     implementation(libs.kotlinStdlib)
     implementation projectOrArtifact(":lifecycle:lifecycle-viewmodel-compose")
 }
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelInFragmentTest.kt b/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelInFragmentTest.kt
index 1e7ebc0..a6ba005 100644
--- a/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelInFragmentTest.kt
+++ b/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelInFragmentTest.kt
@@ -24,7 +24,6 @@
 import androidx.fragment.app.FragmentActivity
 import androidx.fragment.app.FragmentContainerView
 import androidx.lifecycle.ViewModel
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.MediumTest
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -56,7 +55,6 @@
         activity = activityTestRule.activity
     }
 
-    @FlakyTest(bugId = 190608770)
     @Test
     public fun viewModelCreatedInFragment() {
         val fragment = TestFragment(viewModelClass)
diff --git a/navigation/navigation-common/api/current.txt b/navigation/navigation-common/api/current.txt
index faa0cd4..5ccc622 100644
--- a/navigation/navigation-common/api/current.txt
+++ b/navigation/navigation-common/api/current.txt
@@ -487,17 +487,14 @@
     ctor public NavigatorState();
     method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
     method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
-    method public final kotlinx.coroutines.flow.StateFlow<java.util.Map<androidx.navigation.NavBackStackEntry,androidx.navigation.NavigatorState.OnTransitionCompleteListener>> getTransitionsInProgress();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
     method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
-    method public androidx.navigation.NavigatorState.OnTransitionCompleteListener popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
     method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
-    method public androidx.navigation.NavigatorState.OnTransitionCompleteListener pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
     property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
-    property public final kotlinx.coroutines.flow.StateFlow<java.util.Map<androidx.navigation.NavBackStackEntry,androidx.navigation.NavigatorState.OnTransitionCompleteListener>> transitionsInProgress;
-  }
-
-  public static fun interface NavigatorState.OnTransitionCompleteListener {
-    method public void onTransitionComplete();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
   }
 
   @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
diff --git a/navigation/navigation-common/api/public_plus_experimental_current.txt b/navigation/navigation-common/api/public_plus_experimental_current.txt
index faa0cd4..5ccc622 100644
--- a/navigation/navigation-common/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-common/api/public_plus_experimental_current.txt
@@ -487,17 +487,14 @@
     ctor public NavigatorState();
     method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
     method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
-    method public final kotlinx.coroutines.flow.StateFlow<java.util.Map<androidx.navigation.NavBackStackEntry,androidx.navigation.NavigatorState.OnTransitionCompleteListener>> getTransitionsInProgress();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
     method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
-    method public androidx.navigation.NavigatorState.OnTransitionCompleteListener popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
     method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
-    method public androidx.navigation.NavigatorState.OnTransitionCompleteListener pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
     property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
-    property public final kotlinx.coroutines.flow.StateFlow<java.util.Map<androidx.navigation.NavBackStackEntry,androidx.navigation.NavigatorState.OnTransitionCompleteListener>> transitionsInProgress;
-  }
-
-  public static fun interface NavigatorState.OnTransitionCompleteListener {
-    method public void onTransitionComplete();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
   }
 
   @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
diff --git a/navigation/navigation-common/api/restricted_current.txt b/navigation/navigation-common/api/restricted_current.txt
index faa0cd4..5ccc622 100644
--- a/navigation/navigation-common/api/restricted_current.txt
+++ b/navigation/navigation-common/api/restricted_current.txt
@@ -487,17 +487,14 @@
     ctor public NavigatorState();
     method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
     method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
-    method public final kotlinx.coroutines.flow.StateFlow<java.util.Map<androidx.navigation.NavBackStackEntry,androidx.navigation.NavigatorState.OnTransitionCompleteListener>> getTransitionsInProgress();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
     method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
-    method public androidx.navigation.NavigatorState.OnTransitionCompleteListener popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
     method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
-    method public androidx.navigation.NavigatorState.OnTransitionCompleteListener pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
     property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
-    property public final kotlinx.coroutines.flow.StateFlow<java.util.Map<androidx.navigation.NavBackStackEntry,androidx.navigation.NavigatorState.OnTransitionCompleteListener>> transitionsInProgress;
-  }
-
-  public static fun interface NavigatorState.OnTransitionCompleteListener {
-    method public void onTransitionComplete();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
   }
 
   @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt
index 5a96c53..9991594 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt
@@ -337,9 +337,9 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain the argument and it should be null")
-            .that(matchArgs?.getString("myarg"))
-            .isNull()
+        assertWithMessage("Args should not contain the argument")
+            .that(matchArgs?.containsKey("myarg"))
+            .isFalse()
     }
 
     // Ensure case when matching the exact argument query (i.e. param names in braces) is handled
@@ -374,9 +374,9 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain the argument and it should be null")
-            .that(matchArgs?.getString("myarg"))
-            .isNull()
+        assertWithMessage("Args should not contain the argument")
+            .that(matchArgs?.containsKey("myarg"))
+            .isFalse()
     }
 
     @Test
@@ -451,9 +451,9 @@
         assertWithMessage("Args should contain the id")
             .that(matchArgs?.getInt("id"))
             .isEqualTo(id)
-        assertWithMessage("Args should contain optional")
-            .that(matchArgs?.getString("optional"))
-            .isNull()
+        assertWithMessage("Args should not contain optional")
+            .that(matchArgs?.containsKey("optional"))
+            .isFalse()
     }
 
     // Make sure we allow extra params that may not been part of the given deep link
@@ -541,9 +541,9 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain the argument and it should be null")
-            .that(matchArgs?.getString("myarg"))
-            .isNull()
+        assertWithMessage("Args should not contain the argument")
+            .that(matchArgs?.containsKey("myarg"))
+            .isFalse()
     }
 
     @Test
@@ -579,9 +579,9 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain the argument and it should be null")
-            .that(matchArgs?.getString("myarg"))
-            .isNull()
+        assertWithMessage("Args should not contain the argument")
+            .that(matchArgs?.containsKey("myarg"))
+            .isFalse()
     }
 
     @Test
@@ -632,9 +632,9 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain the argument and it should be null")
-            .that(matchArgs?.getString("myarg"))
-            .isNull()
+        assertWithMessage("Args should not contain the argument")
+            .that(matchArgs?.containsKey("myarg"))
+            .isFalse()
     }
 
     @Test
@@ -745,12 +745,12 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain the first name")
-            .that(matchArgs?.getString("first"))
-            .isNull()
-        assertWithMessage("Args should contain the last name")
-            .that(matchArgs?.getString("last"))
-            .isNull()
+        assertWithMessage("Args should not contain the first name")
+            .that(matchArgs?.containsKey("first"))
+            .isFalse()
+        assertWithMessage("Args should not contain the last name")
+            .that(matchArgs?.containsKey("last"))
+            .isFalse()
     }
 
     @Test
@@ -804,9 +804,9 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain arg as null")
-            .that(matchArgs?.getString("myarg"))
-            .isNull()
+        assertWithMessage("Args should not contain the argument")
+            .that(matchArgs?.containsKey("myarg"))
+            .isFalse()
     }
 
     // Handle the case were the input is wild card and separator with no argument
@@ -918,9 +918,9 @@
         assertWithMessage("Args should not be null")
             .that(matchArgs)
             .isNotNull()
-        assertWithMessage("Args should contain the path as null")
-            .that(matchArgs?.getString("path"))
-            .isNull()
+        assertWithMessage("Args should not contain the path")
+            .that(matchArgs?.containsKey("path"))
+            .isFalse()
     }
 
     // Handle the case were the input could be entire path except for the argument
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavigatorState.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavigatorState.kt
index eef5128..c031139 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavigatorState.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavigatorState.kt
@@ -18,7 +18,6 @@
 
 import android.os.Bundle
 import androidx.annotation.RestrictTo
-import androidx.navigation.NavigatorState.OnTransitionCompleteListener
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -32,9 +31,8 @@
 public abstract class NavigatorState {
     private val backStackLock = ReentrantLock(true)
     private val _backStack: MutableStateFlow<List<NavBackStackEntry>> = MutableStateFlow(listOf())
-    private val _transitionsInProgress:
-        MutableStateFlow<Map<NavBackStackEntry, OnTransitionCompleteListener>> =
-            MutableStateFlow(mapOf())
+    private val _transitionsInProgress: MutableStateFlow<Set<NavBackStackEntry>> =
+        MutableStateFlow(setOf())
 
     /**
      * @hide
@@ -52,13 +50,11 @@
     public val backStack: StateFlow<List<NavBackStackEntry>> = _backStack.asStateFlow()
 
     /**
-     * This is the map of currently running transitions to their individual
-     * [OnTransitionCompleteListener]s. Use this map to retrieve the listener and execute the
-     * callback once the transition is complete.
+     * This is the set of currently running transitions. Use this set to retrieve the entry and call
+     * [markTransitionComplete] once the transition is complete.
      */
-    public val transitionsInProgress:
-        StateFlow<Map<NavBackStackEntry, OnTransitionCompleteListener>> =
-            _transitionsInProgress.asStateFlow()
+    public val transitionsInProgress: StateFlow<Set<NavBackStackEntry>> =
+        _transitionsInProgress.asStateFlow()
 
     /**
      * Adds the given [backStackEntry] to the [backStack].
@@ -70,15 +66,25 @@
     }
 
     /**
-     * Provides listener that once activated, adds the given [backStackEntry] to the [backStack].
+     * Adds the given [backStackEntry] to the [backStack]. This also adds the given and
+     * previous entry to the [set of in progress transitions][transitionsInProgress].
+     * Added entries have their [Lifecycle] capped at [Lifecycle.State.STARTED] until an entry is
+     * passed into the [markTransitionComplete] callback, when they are allowed to go to
+     * [Lifecycle.State.RESUMED].
+     *
+     * @see transitionsInProgress
+     * @see markTransitionComplete
+     * @see popWithTransition
      */
-    public open fun pushWithTransition(
-        backStackEntry: NavBackStackEntry
-    ): OnTransitionCompleteListener {
-        push(backStackEntry)
-        return OnTransitionCompleteListener {
-            removeInProgressTransition(backStackEntry)
+    public open fun pushWithTransition(backStackEntry: NavBackStackEntry) {
+        val previousEntry = backStack.value.lastOrNull()
+        // When navigating, we need to mark the outgoing entry as transitioning until it
+        // finishes its outgoing animation.
+        if (previousEntry != null) {
+            _transitionsInProgress.value = _transitionsInProgress.value + previousEntry
         }
+        _transitionsInProgress.value = _transitionsInProgress.value + backStackEntry
+        push(backStackEntry)
     }
 
     /**
@@ -100,50 +106,46 @@
     }
 
     /**
-     * Provides listener that once activated, Pops all destinations up to and including [popUpTo].
+     * Pops all destinations up to and including [popUpTo]. This also adds the given and
+     * incoming entry to the [set of in progress transitions][transitionsInProgress]. Added
+     * entries have their [Lifecycle] held at [Lifecycle.State.CREATED] until an entry is
+     * passed into the [markTransitionComplete] callback, when they are allowed to go to
+     * [Lifecycle.State.DESTROYED] and have their state cleared.
      *
      * This will remove those destinations from the [backStack], saving their state if
      * [saveState] is `true`.
+     *
+     * @see transitionsInProgress
+     * @see markTransitionComplete
+     * @see pushWithTransition
      */
-    public open fun popWithTransition(
-        popUpTo: NavBackStackEntry,
-        saveState: Boolean
-    ): OnTransitionCompleteListener {
-        val listener = OnTransitionCompleteListener {
-            removeInProgressTransition(popUpTo)
+    public open fun popWithTransition(popUpTo: NavBackStackEntry, saveState: Boolean) {
+        _transitionsInProgress.value = _transitionsInProgress.value + popUpTo
+        val incomingEntry = backStack.value.lastOrNull { entry ->
+            entry != popUpTo &&
+                backStack.value.lastIndexOf(entry) < backStack.value.lastIndexOf(popUpTo)
+        }
+        // When popping, we need to mark the incoming entry as transitioning so we keep it
+        // STARTED until the transition completes at which point we can move it to RESUMED
+        if (incomingEntry != null) {
+            _transitionsInProgress.value = _transitionsInProgress.value + incomingEntry
         }
         pop(popUpTo, saveState)
-        return listener
     }
 
     /**
-     * Adds a transition listener to the group of in progress transitions.
+     * This removes the given [NavBackStackEntry] from the [set of the transitions in
+     * progress][transitionsInProgress]. This should be called in conjunction with
+     * [pushWithTransition] and [popWithTransition] as those call are responsible for adding
+     * entries to [transitionsInProgress].
      *
-     * @hide
+     * Failing to call this method could result in entries being prevented from reaching their
+     * final [Lifecycle.State]}.
+     *
+     * @see pushWithTransition
+     * @see popWithTransition
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public fun addInProgressTransition(
-        entry: NavBackStackEntry,
-        listener: OnTransitionCompleteListener
-    ) {
-        _transitionsInProgress.value = _transitionsInProgress.value + (entry to listener)
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public fun removeInProgressTransition(entry: NavBackStackEntry) {
+    public open fun markTransitionComplete(entry: NavBackStackEntry) {
         _transitionsInProgress.value = _transitionsInProgress.value - entry
     }
-
-    /**
-     * OnTransitionCompleteListener receives a callback when a destination transition is complete.
-     */
-    public fun interface OnTransitionCompleteListener {
-        /**
-         * Callback for when the transition has completed.
-         */
-        public fun onTransitionComplete()
-    }
 }
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index 564c176..6448a7e 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -26,15 +26,15 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
 
     implementation(libs.kotlinStdlib)
-    implementation("androidx.compose.foundation:foundation-layout:1.0.0-rc02")
+    implementation("androidx.compose.foundation:foundation-layout:1.0.1")
     api("androidx.activity:activity-compose:1.3.0-rc02")
-    api("androidx.compose.animation:animation:1.0.0-rc02")
-    api(projectOrArtifact(":compose:runtime:runtime"))
-    api(projectOrArtifact(":compose:runtime:runtime-saveable"))
-    api("androidx.compose.ui:ui:1.0.0-rc02")
+    api("androidx.compose.animation:animation:1.0.1")
+    api("androidx.compose.runtime:runtime:1.0.1")
+    api("androidx.compose.runtime:runtime-saveable:1.0.1")
+    api("androidx.compose.ui:ui:1.0.1")
     api("androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07")
     api(projectOrArtifact(":navigation:navigation-runtime-ktx"))
 
diff --git a/navigation/navigation-compose/samples/build.gradle b/navigation/navigation-compose/samples/build.gradle
index 192e019..088d404 100644
--- a/navigation/navigation-compose/samples/build.gradle
+++ b/navigation/navigation-compose/samples/build.gradle
@@ -25,13 +25,13 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
     implementation(libs.kotlinStdlib)
 
     compileOnly(projectOrArtifact(":annotation:annotation-sampled"))
-    implementation("androidx.compose.foundation:foundation:1.0.0-rc02")
+    implementation("androidx.compose.foundation:foundation:1.0.1")
     implementation(projectOrArtifact(":navigation:navigation-compose"))
-    implementation("androidx.compose.material:material:1.0.0-rc02")
+    implementation("androidx.compose.material:material:1.0.1")
 }
 
 androidx {
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/ComposeNavigator.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/ComposeNavigator.kt
index a35f2d0..84a8da4 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/ComposeNavigator.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/ComposeNavigator.kt
@@ -60,6 +60,18 @@
     }
 
     /**
+     * Callback that removes the given [NavBackStackEntry] from the [map of the transitions in
+     * progress][transitionsInProgress]. This should be called in conjunction with [navigate] and
+     * [popBackStack] as those call are responsible for adding entries to [transitionsInProgress].
+     *
+     * Failing to call this method could result in entries being prevented from reaching their
+     * final [Lifecycle.State]}.
+     */
+    internal fun onTransitionComplete(entry: NavBackStackEntry) {
+        state.markTransitionComplete(entry)
+    }
+
+    /**
      * NavDestination specific to [ComposeNavigator]
      */
     @NavDestination.ClassType(Composable::class)
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
index 62dc370..986422c 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
@@ -118,7 +118,7 @@
     val backStack by composeNavigator.backStack.collectAsState()
     val transitionsInProgress by composeNavigator.transitionsInProgress.collectAsState()
 
-    val backStackEntry = transitionsInProgress.keys.lastOrNull { entry ->
+    val backStackEntry = transitionsInProgress.lastOrNull { entry ->
         entry.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
     } ?: backStack.lastOrNull { entry ->
         entry.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
@@ -137,13 +137,13 @@
                     // There's no animation for the initial crossfade,
                     // so we can instantly mark the transition as complete
                     transitionsInProgress.forEach { entry ->
-                        entry.value.onTransitionComplete()
+                        composeNavigator.onTransitionComplete(entry)
                     }
                     initialCrossfade = false
                 }
                 onDispose {
                     transitionsInProgress.forEach { entry ->
-                        entry.value.onTransitionComplete()
+                        composeNavigator.onTransitionComplete(entry)
                     }
                 }
             }
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt
index 9706a9a..10147067 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigatorDestinationBuilder.kt
@@ -122,7 +122,8 @@
      * DSL for constructing a new [DialogFragmentNavigator.Destination]
      *
      * @param navigator navigator used to create the destination
-     * @param route the destination's unique route
+     * @param route the destination's unique route. This sets the [route] on the newly
+     * constructed [NavDestination]. This can be any valid non-empty String.
      * @param fragmentClass the class name of the DialogFragment to show when you navigate to this
      * destination
      */
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
index b061ee6..63c95d2 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
@@ -388,10 +388,55 @@
             .isTrue()
     }
 
-    private fun createNavController(): NavController {
+    @Suppress("DEPRECATION")
+    @UiThreadTest
+    @Test
+    fun testOnClearedWhenHostClearedAfterSaveStateWithTransitions() {
+        val hostStore = ViewModelStore()
+        val navController = createNavController(true)
+        navController.setViewModelStore(hostStore)
+        val navGraph = navController.navigatorProvider.navigation(
+            id = 1,
+            startDestination = R.id.start_test
+        ) {
+            test(R.id.start_test)
+        }
+        navController.setGraph(navGraph, null)
+
+        val owner = navController.getBackStackEntry(R.id.start_test)
+        assertThat(owner).isNotNull()
+        val viewModel: TestAndroidViewModel = ViewModelProvider(owner).get()
+        assertThat(viewModel.isCleared).isFalse()
+
+        // Navigate to a new instance of start_test, popping the previous one and saving state
+        navController.navigate(
+            R.id.start_test,
+            null,
+            navOptions {
+                popUpTo(R.id.start_test) {
+                    inclusive = true
+                    saveState = true
+                }
+            }
+        )
+        val newEntry = navController.getBackStackEntry(R.id.start_test)
+        navController.navigatorProvider[TestNavigator::class].onTransitionComplete(newEntry)
+
+        assertWithMessage("ViewModel should be saved when the destination is saved")
+            .that(viewModel.isCleared)
+            .isFalse()
+
+        hostStore.clear()
+
+        assertWithMessage("ViewModel should be cleared when the host is cleared")
+            .that(viewModel.isCleared)
+            .isTrue()
+    }
+
+    private fun createNavController(withTransitions: Boolean = false): NavController {
         val navController = NavHostController(ApplicationProvider.getApplicationContext())
         navController.setLifecycleOwner(TestLifecycleOwner())
-        val navigator = TestNavigator()
+        val navigator = TestNavigator(withTransitions)
         navController.navigatorProvider.addNavigator(navigator)
         return navController
     }
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index de23adb..b80a225 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -181,6 +181,7 @@
         mutableMapOf<Navigator<out NavDestination>, NavControllerNavigatorState>()
     private var addToBackStackHandler: ((backStackEntry: NavBackStackEntry) -> Unit)? = null
     private var popFromBackStackHandler: ((popUpTo: NavBackStackEntry) -> Unit)? = null
+    private val entrySavedState = mutableMapOf<NavBackStackEntry, Boolean>()
 
     /**
      * Call [Navigator.navigate] while setting up a [handler] that receives callbacks
@@ -269,43 +270,28 @@
             }
         }
 
-        override fun pushWithTransition(
-            backStackEntry: NavBackStackEntry
-        ): OnTransitionCompleteListener {
-            val innerListener = super.pushWithTransition(backStackEntry)
-            val listener = OnTransitionCompleteListener {
-                innerListener.onTransitionComplete()
-                if (!this@NavControllerNavigatorState.isNavigating) {
-                    updateBackStackLifecycle()
-                }
-            }
-            addInProgressTransition(backStackEntry, listener)
-            return listener
+        override fun popWithTransition(popUpTo: NavBackStackEntry, saveState: Boolean) {
+            super.popWithTransition(popUpTo, saveState)
+            entrySavedState[popUpTo] = saveState
         }
 
-        override fun popWithTransition(
-            popUpTo: NavBackStackEntry,
-            saveState: Boolean
-        ): OnTransitionCompleteListener {
-            // we need to mark the entry as transitioning before making the super call to pop so
-            // we don't move its lifecycle to DESTROYED.
-            addInProgressTransition(popUpTo) { }
-            val innerListener = super.popWithTransition(popUpTo, saveState)
-            val listener = OnTransitionCompleteListener {
-                innerListener.onTransitionComplete()
-                if (backQueue.contains(popUpTo)) {
-                    updateBackStackLifecycle()
-                } else {
-                    // If the entry is no longer part of the backStack, we need to manually move
-                    // it to DESTROYED, and clear its view model
-                    popUpTo.maxLifecycle = Lifecycle.State.DESTROYED
-                    if (!saveState) {
-                        viewModel?.clear(popUpTo.id)
-                    }
+        override fun markTransitionComplete(entry: NavBackStackEntry) {
+            val savedState = entrySavedState[entry] == true
+            super.markTransitionComplete(entry)
+            entrySavedState.remove(entry)
+            if (!backQueue.contains(entry)) {
+                // If the entry is no longer part of the backStack, we need to manually move
+                // it to DESTROYED, and clear its view model
+                entry.maxLifecycle = Lifecycle.State.DESTROYED
+                if (!savedState) {
+                    viewModel?.clear(entry.id)
                 }
+                updateBackStackLifecycle()
+            } else if (!this@NavControllerNavigatorState.isNavigating) {
+                updateBackStackLifecycle()
             }
-            addInProgressTransition(popUpTo, listener)
-            return listener
+            // else, updateBackStackLifecycle() will be called after any ongoing navigate() call
+            // completes
         }
     }
 
@@ -604,17 +590,7 @@
         val navigator = navigatorProvider
             .getNavigator<Navigator<NavDestination>>(entry.destination.navigatorName)
         val state = navigatorState[navigator]
-        val transitioning = state?.transitionsInProgress?.value?.containsKey(entry)
-        // When popping, we need to mark the incoming entry as transitioning so we keep it
-        // STARTED until the transition completes at which point we can move it to RESUMED
-        if (backQueue.isNotEmpty() && transitioning == true) {
-            state.addInProgressTransition(backQueue.last()) {
-                state.removeInProgressTransition(backQueue.last())
-                if (!state.isNavigating) {
-                    updateBackStackLifecycle()
-                }
-            }
-        }
+        val transitioning = state?.transitionsInProgress?.value?.contains(entry)
         if (entry.lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
             if (saveState) {
                 // Move the state through STOPPED
@@ -862,7 +838,7 @@
                     val navigator = navigatorProvider
                         .getNavigator<Navigator<*>>(entry.destination.navigatorName)
                     val state = navigatorState[navigator]
-                    val transitioning = state?.transitionsInProgress?.value?.containsKey(entry)
+                    val transitioning = state?.transitionsInProgress?.value?.contains(entry)
                     if (transitioning != true) {
                         upwardStateTransitions[entry] = Lifecycle.State.RESUMED
                     } else {
diff --git a/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavigatorStateTest.kt b/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavigatorStateTest.kt
index 0685776..7be84d4 100644
--- a/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavigatorStateTest.kt
+++ b/navigation/navigation-testing/src/androidTest/java/androidx/navigation/testing/TestNavigatorStateTest.kt
@@ -103,7 +103,7 @@
         assertThat(firstEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.STARTED)
 
-        state.transitionsInProgress.value[firstEntry]?.onTransitionComplete()
+        state.markTransitionComplete(firstEntry)
         assertThat(firstEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.RESUMED)
 
@@ -113,23 +113,38 @@
             .isEqualTo(Lifecycle.State.CREATED)
         assertThat(secondEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.STARTED)
+        assertThat(state.transitionsInProgress.value.contains(firstEntry)).isTrue()
 
-        state.transitionsInProgress.value[secondEntry]?.onTransitionComplete()
+        state.markTransitionComplete(firstEntry)
+        state.markTransitionComplete(secondEntry)
         assertThat(secondEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.RESUMED)
 
-        navigator.popBackStack(secondEntry, false)
+        navigator.popBackStack(secondEntry, true)
         assertThat(secondEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.CREATED)
         assertThat(firstEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.STARTED)
 
-        state.transitionsInProgress.value[firstEntry]?.onTransitionComplete()
+        state.markTransitionComplete(firstEntry)
         assertThat(firstEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.RESUMED)
-        state.transitionsInProgress.value[secondEntry]?.onTransitionComplete()
+        state.markTransitionComplete(secondEntry)
         assertThat(secondEntry.lifecycle.currentState)
             .isEqualTo(Lifecycle.State.DESTROYED)
+
+        val restoredSecondEntry = state.restoreBackStackEntry(secondEntry)
+        navigator.navigate(listOf(restoredSecondEntry), null, null)
+        assertThat(firstEntry.lifecycle.currentState)
+            .isEqualTo(Lifecycle.State.CREATED)
+        assertThat(restoredSecondEntry.lifecycle.currentState)
+            .isEqualTo(Lifecycle.State.STARTED)
+        assertThat(state.transitionsInProgress.value.contains(firstEntry)).isTrue()
+
+        state.markTransitionComplete(firstEntry)
+        state.markTransitionComplete(restoredSecondEntry)
+        assertThat(restoredSecondEntry.lifecycle.currentState)
+            .isEqualTo(Lifecycle.State.RESUMED)
     }
 
     @Navigator.Name("test")
diff --git a/navigation/navigation-testing/src/main/java/androidx/navigation/testing/TestNavigatorState.kt b/navigation/navigation-testing/src/main/java/androidx/navigation/testing/TestNavigatorState.kt
index eef9c77..c146ce31 100644
--- a/navigation/navigation-testing/src/main/java/androidx/navigation/testing/TestNavigatorState.kt
+++ b/navigation/navigation-testing/src/main/java/androidx/navigation/testing/TestNavigatorState.kt
@@ -27,7 +27,6 @@
 import androidx.navigation.NavDestination
 import androidx.navigation.NavViewModelStoreProvider
 import androidx.navigation.NavigatorState
-import androidx.navigation.NavigatorState.OnTransitionCompleteListener
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
@@ -66,6 +65,7 @@
     }
 
     private val savedStates = mutableMapOf<String, Bundle>()
+    private val entrySavedState = mutableMapOf<NavBackStackEntry, Boolean>()
 
     override fun createBackStackEntry(
         destination: NavDestination,
@@ -96,19 +96,6 @@
         updateMaxLifecycle()
     }
 
-    override fun pushWithTransition(
-        backStackEntry: NavBackStackEntry
-    ): OnTransitionCompleteListener {
-        val innerListener = super.pushWithTransition(backStackEntry)
-        val listener = OnTransitionCompleteListener {
-            innerListener.onTransitionComplete()
-            updateMaxLifecycle()
-        }
-        addInProgressTransition(backStackEntry, listener)
-        updateMaxLifecycle()
-        return listener
-    }
-
     override fun pop(popUpTo: NavBackStackEntry, saveState: Boolean) {
         val beforePopList = backStack.value
         val poppedList = beforePopList.subList(beforePopList.indexOf(popUpTo), beforePopList.size)
@@ -116,35 +103,20 @@
         updateMaxLifecycle(poppedList, saveState)
     }
 
-    override fun popWithTransition(
-        popUpTo: NavBackStackEntry,
-        saveState: Boolean
-    ): OnTransitionCompleteListener {
-        // Get the entry that will be incoming after we have popped all the way up to the desired
-        // entry.
-        // We need to do this before we call popWithTransition because for the TestNavigatorState
-        // pop is called immediately, which would cause the entry to immediately go to RESUMED.
-        val incomingEntry = backStack.value.lastOrNull { entry ->
-            entry != popUpTo &&
-                backStack.value.lastIndexOf(entry) < backStack.value.lastIndexOf(popUpTo)
+    override fun popWithTransition(popUpTo: NavBackStackEntry, saveState: Boolean) {
+        super.popWithTransition(popUpTo, saveState)
+        entrySavedState[popUpTo] = saveState
+    }
+
+    override fun markTransitionComplete(entry: NavBackStackEntry) {
+        val savedState = entrySavedState[entry] == true
+        super.markTransitionComplete(entry)
+        entrySavedState.remove(entry)
+        if (!backStack.value.contains(entry)) {
+            updateMaxLifecycle(listOf(entry), savedState)
+        } else {
+            updateMaxLifecycle()
         }
-        // When popping, we need to mark the incoming entry as transitioning so we keep it
-        // STARTED until the transition completes at which point we can move it to RESUMED
-        if (incomingEntry != null) {
-            addInProgressTransition(incomingEntry) {
-                removeInProgressTransition(incomingEntry)
-                updateMaxLifecycle()
-            }
-        }
-        addInProgressTransition(popUpTo) { }
-        val innerListener = super.popWithTransition(popUpTo, saveState)
-        val listener = OnTransitionCompleteListener {
-            innerListener.onTransitionComplete()
-            updateMaxLifecycle(listOf(popUpTo))
-        }
-        addInProgressTransition(popUpTo, listener)
-        updateMaxLifecycle()
-        return listener
     }
 
     private fun updateMaxLifecycle(
@@ -158,16 +130,22 @@
             withContext(Dispatchers.Main.immediate) {
                 // Mark all removed NavBackStackEntries as DESTROYED
                 for (entry in poppedList.reversed()) {
-                    if (saveState) {
+                    if (
+                        saveState &&
+                        entry.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
+                    ) {
                         // Move the NavBackStackEntry to the stopped state, then save its state
                         entry.maxLifecycle = Lifecycle.State.CREATED
                         val savedState = Bundle()
                         entry.saveState(savedState)
                         savedStates[entry.id] = savedState
                     }
-                    val transitioning = transitionsInProgress.value.containsKey(entry)
+                    val transitioning = transitionsInProgress.value.contains(entry)
                     if (!transitioning) {
                         entry.maxLifecycle = Lifecycle.State.DESTROYED
+                        if (!saveState) {
+                            savedStates.remove(entry.id)
+                        }
                     } else {
                         entry.maxLifecycle = Lifecycle.State.CREATED
                     }
@@ -176,7 +154,7 @@
                 val currentList = backStack.value
                 var previousEntry: NavBackStackEntry? = null
                 for (entry in currentList.reversed()) {
-                    val transitioning = transitionsInProgress.value.containsKey(entry)
+                    val transitioning = transitionsInProgress.value.contains(entry)
                     entry.maxLifecycle = when {
                         previousEntry == null ->
                             if (!transitioning) {
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index f6892b8..de8ffd0 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -28,10 +28,10 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
 
     implementation(libs.kotlinStdlib)
-    api(projectOrArtifact(":compose:foundation:foundation"))
+    api("androidx.compose.foundation:foundation:1.0.1")
     api("androidx.paging:paging-common:3.0.0")
 
     androidTestImplementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
diff --git a/paging/paging-compose/samples/build.gradle b/paging/paging-compose/samples/build.gradle
index 8bd229e..5b7a3c1 100644
--- a/paging/paging-compose/samples/build.gradle
+++ b/paging/paging-compose/samples/build.gradle
@@ -26,12 +26,12 @@
 }
 
 dependencies {
-    kotlinPlugin(projectOrArtifact(":compose:compiler:compiler"))
+    kotlinPlugin("androidx.compose.compiler:compiler:1.0.1")
     implementation(libs.kotlinStdlib)
 
     compileOnly(projectOrArtifact(":annotation:annotation-sampled"))
-    implementation("androidx.compose.foundation:foundation:1.0.0-rc02")
-    implementation("androidx.compose.material:material:1.0.0-rc02")
+    implementation("androidx.compose.foundation:foundation:1.0.1")
+    implementation("androidx.compose.material:material:1.0.1")
     implementation(projectOrArtifact(":paging:paging-compose"))
 }
 
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt
index aec2c6e..8e0b73e 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt
@@ -30,11 +30,14 @@
 import androidx.room.Insert
 import androidx.room.PrimaryKey
 import androidx.room.Query
+import androidx.room.RawQuery
 import androidx.room.Room
 import androidx.room.RoomDatabase
 import androidx.room.awaitPendingRefresh
 import androidx.room.pendingRefresh
 import androidx.room.refreshRunnable
+import androidx.sqlite.db.SimpleSQLiteQuery
+import androidx.sqlite.db.SupportSQLiteQuery
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.espresso.base.MainThread
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -162,6 +165,43 @@
     }
 
     @Test
+    fun loadEverythingRawQuery() {
+        // open db
+        val items = createItems(startId = 15, count = 50)
+        db.dao.insert(items)
+        val query = SimpleSQLiteQuery(
+            "SELECT * FROM PagingEntity ORDER BY id ASC"
+        )
+        val pager = Pager(
+            config = CONFIG,
+            pagingSourceFactory = { db.dao.loadItemsRaw(query) }
+        )
+        runTest(pager = pager) {
+            itemStore.awaitInitialLoad()
+            assertThat(
+                itemStore.peekItems()
+            ).containsExactlyElementsIn(
+                items.createExpected(
+                    fromIndex = 0,
+                    toIndex = CONFIG.initialLoadSize
+                )
+            )
+            // now access more items that should trigger loading more
+            withContext(Dispatchers.Main) {
+                itemStore.get(20)
+            }
+            assertThat(itemStore.awaitItem(20)).isEqualTo(items[20])
+            // now access to the end of the list, it should load everything as we disabled jumping
+            withContext(Dispatchers.Main) {
+                itemStore.get(items.size - 1)
+            }
+            assertThat(itemStore.awaitItem(items.size - 1)).isEqualTo(items.last())
+            assertThat(itemStore.peekItems()).isEqualTo(items)
+            assertThat(itemStore.currentGenerationId).isEqualTo(1)
+        }
+    }
+
+    @Test
     fun loadEverything_inReverse() {
         // open db
         val items = createItems(startId = 0, count = 100)
@@ -200,6 +240,210 @@
     }
 
     @Test
+    fun loadEverythingRawQuery_inReverse() {
+        // open db
+        val items = createItems(startId = 0, count = 100)
+        db.dao.insert(items)
+        val query = SimpleSQLiteQuery(
+            "SELECT * FROM PagingEntity ORDER BY id ASC"
+        )
+        val pager = Pager(
+            config = CONFIG,
+            initialKey = 98,
+            pagingSourceFactory = { db.dao.loadItemsRaw(query) }
+        )
+        runTest(pager) {
+            itemStore.awaitInitialLoad()
+            assertThat(
+                itemStore.peekItems()
+            ).containsExactlyElementsIn(
+                items.createExpected(
+                    // Paging 3 implementation loads starting from initial key
+                    fromIndex = 98,
+                    toIndex = 100
+                )
+            )
+            // now access more items that should trigger loading more
+            withContext(Dispatchers.Main) {
+                itemStore.get(40)
+            }
+            assertThat(itemStore.awaitItem(40)).isEqualTo(items[40])
+            // now access to the beginning of the list, it should load everything as we don't
+            // support jumping
+            withContext(Dispatchers.Main) {
+                itemStore.get(0)
+            }
+            assertThat(itemStore.awaitItem(0)).isEqualTo(items[0])
+            assertThat(itemStore.peekItems()).isEqualTo(items)
+            assertThat(itemStore.currentGenerationId).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun rawQuery_userSuppliedLimitOffset() {
+        val items = createItems(startId = 15, count = 70)
+        db.dao.insert(items)
+
+        val query = SimpleSQLiteQuery(
+            "SELECT * FROM PagingEntity ORDER BY id ASC LIMIT 30 OFFSET 5"
+        )
+        val pager = Pager(
+            config = CONFIG,
+            pagingSourceFactory = { db.dao.loadItemsRaw(query) }
+        )
+
+        runTest(pager = pager) {
+            itemStore.awaitInitialLoad()
+            assertThat(
+                itemStore.peekItems()
+            ).containsExactlyElementsIn(
+                // returns items 20 to 28 with 21 null place holders after
+                items.createBoundedExpected(
+                    fromIndex = 5,
+                    toIndex = 5 + CONFIG.initialLoadSize,
+                    toPlaceholderIndex = 35,
+                )
+            )
+            // now access more items that should trigger loading more
+            withContext(Dispatchers.Main) {
+                itemStore.get(15)
+            }
+            // item 15 is offset by 5 = 20
+            assertThat(itemStore.awaitItem(15)).isEqualTo(items[20])
+            // normally itemStore.get(50) is valid, but user-set LIMIT should bound item count to 30
+            // itemStore.get(50) should now become invalid
+            val expectedException = assertFailsWith<IndexOutOfBoundsException> {
+                withContext(Dispatchers.Main) {
+                    itemStore.get(50)
+                }
+            }
+            assertThat(expectedException.message).isEqualTo("Index: 50, Size: 30")
+            assertThat(itemStore.currentGenerationId).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun rawQuery_multipleArguments() {
+        val items = createItems(startId = 0, count = 80)
+        db.dao.insert(items)
+        val query = SimpleSQLiteQuery(
+            "SELECT * " +
+                "FROM PagingEntity " +
+                "WHERE id > 49 AND id < 76 " +
+                "ORDER BY id ASC " +
+                "LIMIT 20"
+        )
+        val pager = Pager(
+            config = CONFIG,
+            pagingSourceFactory = { db.dao.loadItemsRaw(query) }
+        )
+
+        runTest(pager = pager) {
+            itemStore.awaitInitialLoad()
+            assertThat(
+                itemStore.peekItems()
+            ).containsExactlyElementsIn(
+                // returns items 50 to 58 with 11 null place holders after
+                items.createBoundedExpected(
+                    fromIndex = 50,
+                    toIndex = 50 + CONFIG.initialLoadSize,
+                    toPlaceholderIndex = 70,
+                )
+            )
+            // now access more items that should trigger loading more
+            withContext(Dispatchers.Main) {
+                itemStore.get(15)
+            }
+            // item 15 is offset by 50 because of `WHERE id > 49` arg
+            assertThat(itemStore.awaitItem(15)).isEqualTo(items[65])
+            // normally itemStore.get(50) is valid, but user-set LIMIT should bound item count to 20
+            val expectedException = assertFailsWith<IndexOutOfBoundsException> {
+                withContext(Dispatchers.Main) {
+                    itemStore.get(50)
+                }
+            }
+            assertThat(expectedException.message).isEqualTo("Index: 50, Size: 20")
+            assertThat(itemStore.currentGenerationId).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun keyTooLarge_returnLastPage() {
+        val items = createItems(startId = 0, count = 50)
+        db.dao.insert(items)
+
+        val pager = Pager(
+            config = CONFIG,
+            initialKey = 80
+        ) {
+            db.dao.loadItems()
+        }
+        runTest(pager = pager) {
+            itemStore.awaitInitialLoad()
+            assertThat(
+                itemStore.peekItems()
+            ).containsExactlyElementsIn(
+                // should return last page when key is too large
+                items.createExpected(
+                    fromIndex = 41,
+                    toIndex = 50,
+                )
+            )
+            // now trigger a prepend
+            withContext(Dispatchers.Main) {
+                itemStore.get(20)
+            }
+            assertThat(itemStore.awaitItem(20)).isEqualTo(items[20])
+        }
+    }
+
+    @Test
+    fun jumping() {
+        val items = createItems(startId = 0, count = 200)
+        db.dao.insert(items)
+
+        val config = PagingConfig(
+            pageSize = 3,
+            initialLoadSize = 9,
+            enablePlaceholders = true,
+            jumpThreshold = 80
+        )
+        val pager = Pager(
+            config = config,
+        ) {
+            db.dao.loadItems()
+        }
+        runTest(pager = pager) {
+            itemStore.awaitInitialLoad()
+            assertThat(
+                itemStore.peekItems()
+            ).containsExactlyElementsIn(
+                items.createExpected(
+                    fromIndex = 0,
+                    toIndex = config.initialLoadSize,
+                )
+            )
+            // now trigger a jump, accessed index needs to be larger than jumpThreshold
+            withContext(Dispatchers.Main) {
+                itemStore.get(120)
+            }
+            // the jump should trigger a refresh load with new generation
+            itemStore.awaitGeneration(2)
+            itemStore.awaitInitialLoad()
+            // the refresh should load around anchorPosition of 120, with refresh key as 116
+            // and null placeholders before and after
+            assertThat(
+                itemStore.peekItems()
+            ).containsExactlyElementsIn(
+                items.createExpected(
+                    fromIndex = 116,
+                    toIndex = 116 + config.initialLoadSize,
+                )
+            )
+        }
+    }
+
+    @Test
     fun prependWithDelayedInvalidation() {
         val items = createItems(startId = 0, count = 90)
         db.dao.insert(items)
@@ -441,7 +685,7 @@
      */
     private fun List<PagingEntity?>.createExpected(
         fromIndex: Int,
-        toIndex: Int
+        toIndex: Int,
     ): List<PagingEntity?> {
         val result = mutableListOf<PagingEntity?>()
         (0 until fromIndex).forEach { _ -> result.add(null) }
@@ -450,6 +694,17 @@
         return result
     }
 
+    private fun List<PagingEntity?>.createBoundedExpected(
+        fromIndex: Int,
+        toIndex: Int,
+        toPlaceholderIndex: Int,
+    ): List<PagingEntity?> {
+        val result = mutableListOf<PagingEntity?>()
+        result.addAll(this.subList(fromIndex, toIndex))
+        (toIndex until toPlaceholderIndex).forEach { _ -> result.add(null) }
+        return result
+    }
+
     @Database(
         version = 1,
         exportSchema = false,
@@ -497,6 +752,9 @@
 
         @Query("SELECT * FROM PagingEntity ORDER BY id ASC")
         fun loadItems(): PagingSource<Int, PagingEntity>
+
+        @RawQuery(observedEntities = [PagingEntity::class])
+        fun loadItemsRaw(query: SupportSQLiteQuery): PagingSource<Int, PagingEntity>
     }
 
     /**
@@ -683,7 +941,7 @@
         private val CONFIG = PagingConfig(
             pageSize = 3,
             initialLoadSize = 9,
-            enablePlaceholders = true
+            enablePlaceholders = true,
         )
     }
 }
diff --git a/settings.gradle b/settings.gradle
index 59dc14e..d312fea 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -631,6 +631,7 @@
 includeProject(":wear:compose:compose-foundation-samples", "wear/compose/compose-foundation/samples", [BuildType.COMPOSE])
 includeProject(":wear:compose:compose-material", "wear/compose/compose-material", [BuildType.COMPOSE])
 includeProject(":wear:compose:compose-material-benchmark", "wear/compose/compose-material/benchmark", [BuildType.COMPOSE])
+includeProject(":wear:compose:compose-material-samples", "wear/compose/compose-material/samples", [BuildType.COMPOSE])
 includeProject(":wear:compose:integration-tests:demos", "wear/compose/integration-tests/demos", [BuildType.COMPOSE])
 includeProject(":wear:compose:integration-tests:macrobenchmark", "wear/compose/integration-tests/macrobenchmark", [BuildType.COMPOSE])
 includeProject(":wear:compose:integration-tests:macrobenchmark-target", "wear/compose/integration-tests/macrobenchmark-target", [BuildType.COMPOSE])
diff --git a/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigator.kt b/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigator.kt
index f4b4933..6adce96 100644
--- a/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigator.kt
+++ b/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigator.kt
@@ -18,13 +18,15 @@
 
 import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavDestination
+import androidx.navigation.NavOptions
 import androidx.navigation.Navigator
 
 /**
  * A simple Navigator that doesn't actually navigate anywhere, but does dispatch correctly
  */
 @Navigator.Name("test")
-open class TestNavigator : Navigator<TestNavigator.Destination>() {
+open class TestNavigator(private val hasTransitions: Boolean = false) :
+    Navigator<TestNavigator.Destination>() {
 
     val backStack: List<NavBackStackEntry>
         get() = state.backStack.value
@@ -41,6 +43,32 @@
         return Destination(this)
     }
 
+    override fun navigate(
+        entries: List<NavBackStackEntry>,
+        navOptions: NavOptions?,
+        navigatorExtras: Extras?
+    ) {
+        entries.forEach { entry ->
+            if (hasTransitions) {
+                state.pushWithTransition(entry)
+            } else {
+                state.push(entry)
+            }
+        }
+    }
+
+    override fun popBackStack(popUpTo: NavBackStackEntry, savedState: Boolean) {
+        if (hasTransitions) {
+            state.popWithTransition(popUpTo, savedState)
+        } else {
+            super.popBackStack(popUpTo, savedState)
+        }
+    }
+
+    public fun onTransitionComplete(entry: NavBackStackEntry) {
+        state.markTransitionComplete(entry)
+    }
+
     /**
      * A simple Test destination
      */
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index 5f663c0..ec09db5 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -33,7 +33,7 @@
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter cardBackgroundPainter(optional long startBackgroundColor, optional long endBackgroundColor, optional androidx.compose.ui.unit.LayoutDirection gradientDirection);
     method public float getAppImageSize();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
     property public final float AppImageSize;
     property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
     field public static final androidx.wear.compose.material.CardDefaults INSTANCE;
@@ -138,6 +138,10 @@
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  public final class ListHeaderKt {
+    method @androidx.compose.runtime.Composable public static void ListHeader(optional androidx.compose.ui.Modifier modifier, optional long contentColor, optional long backgroundColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
   public final class MaterialTextSelectionColorsKt {
   }
 
@@ -155,6 +159,55 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material.Colors colors, optional androidx.wear.compose.material.Typography typography, optional androidx.wear.compose.material.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class ScalingLazyColumnDefaults {
+    method public androidx.wear.compose.material.ScalingParams scalingParams(optional float edgeScale, optional float edgeAlpha, optional float minElementHeight, optional float maxElementHeight, optional float minTransitionArea, optional float maxTransitionArea, optional androidx.compose.animation.core.Easing scaleInterpolator, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Constraints,java.lang.Integer> viewportVerticalOffsetResolver);
+    field public static final androidx.wear.compose.material.ScalingLazyColumnDefaults INSTANCE;
+  }
+
+  public final class ScalingLazyColumnKt {
+    method @androidx.compose.runtime.Composable public static void ScalingLazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.ScalingParams scalingParams, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.wear.compose.material.ScalingLazyColumnState state, kotlin.jvm.functions.Function1<? super androidx.wear.compose.material.ScalingLazyColumnScope,kotlin.Unit> content);
+  }
+
+  public final class ScalingLazyColumnMeasureKt {
+  }
+
+  public interface ScalingLazyColumnScope {
+    method public void item(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method public void items(int count, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> itemContent);
+  }
+
+  @androidx.compose.runtime.Stable public final class ScalingLazyColumnState {
+    ctor public ScalingLazyColumnState();
+    field public static final androidx.wear.compose.material.ScalingLazyColumnState.Companion Companion;
+  }
+
+  public static final class ScalingLazyColumnState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.wear.compose.material.ScalingLazyColumnState,?> getSaver();
+    property public final androidx.compose.runtime.saveable.Saver<androidx.wear.compose.material.ScalingLazyColumnState,?> Saver;
+  }
+
+  public final class ScalingLazyColumnStateKt {
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.ScalingLazyColumnState rememberScalingLazyColumnState();
+  }
+
+  public interface ScalingParams {
+    method public float getEdgeAlpha();
+    method public float getEdgeScale();
+    method public float getMaxElementHeight();
+    method public float getMaxTransitionArea();
+    method public float getMinElementHeight();
+    method public float getMinTransitionArea();
+    method public androidx.compose.animation.core.Easing getScaleInterpolator();
+    method public int resolveViewportVerticalOffset(long viewportConstraints);
+    property public abstract float edgeAlpha;
+    property public abstract float edgeScale;
+    property public abstract float maxElementHeight;
+    property public abstract float maxTransitionArea;
+    property public abstract float minElementHeight;
+    property public abstract float minTransitionArea;
+    property public abstract androidx.compose.animation.core.Easing scaleInterpolator;
+  }
+
   @androidx.compose.runtime.Immutable public final class Shapes {
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.wear.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
diff --git a/wear/compose/compose-material/api/public_plus_experimental_current.txt b/wear/compose/compose-material/api/public_plus_experimental_current.txt
index 5f663c0..ec09db5 100644
--- a/wear/compose/compose-material/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material/api/public_plus_experimental_current.txt
@@ -33,7 +33,7 @@
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter cardBackgroundPainter(optional long startBackgroundColor, optional long endBackgroundColor, optional androidx.compose.ui.unit.LayoutDirection gradientDirection);
     method public float getAppImageSize();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
     property public final float AppImageSize;
     property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
     field public static final androidx.wear.compose.material.CardDefaults INSTANCE;
@@ -138,6 +138,10 @@
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  public final class ListHeaderKt {
+    method @androidx.compose.runtime.Composable public static void ListHeader(optional androidx.compose.ui.Modifier modifier, optional long contentColor, optional long backgroundColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
   public final class MaterialTextSelectionColorsKt {
   }
 
@@ -155,6 +159,55 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material.Colors colors, optional androidx.wear.compose.material.Typography typography, optional androidx.wear.compose.material.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class ScalingLazyColumnDefaults {
+    method public androidx.wear.compose.material.ScalingParams scalingParams(optional float edgeScale, optional float edgeAlpha, optional float minElementHeight, optional float maxElementHeight, optional float minTransitionArea, optional float maxTransitionArea, optional androidx.compose.animation.core.Easing scaleInterpolator, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Constraints,java.lang.Integer> viewportVerticalOffsetResolver);
+    field public static final androidx.wear.compose.material.ScalingLazyColumnDefaults INSTANCE;
+  }
+
+  public final class ScalingLazyColumnKt {
+    method @androidx.compose.runtime.Composable public static void ScalingLazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.ScalingParams scalingParams, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.wear.compose.material.ScalingLazyColumnState state, kotlin.jvm.functions.Function1<? super androidx.wear.compose.material.ScalingLazyColumnScope,kotlin.Unit> content);
+  }
+
+  public final class ScalingLazyColumnMeasureKt {
+  }
+
+  public interface ScalingLazyColumnScope {
+    method public void item(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method public void items(int count, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> itemContent);
+  }
+
+  @androidx.compose.runtime.Stable public final class ScalingLazyColumnState {
+    ctor public ScalingLazyColumnState();
+    field public static final androidx.wear.compose.material.ScalingLazyColumnState.Companion Companion;
+  }
+
+  public static final class ScalingLazyColumnState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.wear.compose.material.ScalingLazyColumnState,?> getSaver();
+    property public final androidx.compose.runtime.saveable.Saver<androidx.wear.compose.material.ScalingLazyColumnState,?> Saver;
+  }
+
+  public final class ScalingLazyColumnStateKt {
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.ScalingLazyColumnState rememberScalingLazyColumnState();
+  }
+
+  public interface ScalingParams {
+    method public float getEdgeAlpha();
+    method public float getEdgeScale();
+    method public float getMaxElementHeight();
+    method public float getMaxTransitionArea();
+    method public float getMinElementHeight();
+    method public float getMinTransitionArea();
+    method public androidx.compose.animation.core.Easing getScaleInterpolator();
+    method public int resolveViewportVerticalOffset(long viewportConstraints);
+    property public abstract float edgeAlpha;
+    property public abstract float edgeScale;
+    property public abstract float maxElementHeight;
+    property public abstract float maxTransitionArea;
+    property public abstract float minElementHeight;
+    property public abstract float minTransitionArea;
+    property public abstract androidx.compose.animation.core.Easing scaleInterpolator;
+  }
+
   @androidx.compose.runtime.Immutable public final class Shapes {
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.wear.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index 5f663c0..ec09db5 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -33,7 +33,7 @@
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter cardBackgroundPainter(optional long startBackgroundColor, optional long endBackgroundColor, optional androidx.compose.ui.unit.LayoutDirection gradientDirection);
     method public float getAppImageSize();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
     property public final float AppImageSize;
     property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
     field public static final androidx.wear.compose.material.CardDefaults INSTANCE;
@@ -138,6 +138,10 @@
     method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
+  public final class ListHeaderKt {
+    method @androidx.compose.runtime.Composable public static void ListHeader(optional androidx.compose.ui.Modifier modifier, optional long contentColor, optional long backgroundColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
   public final class MaterialTextSelectionColorsKt {
   }
 
@@ -155,6 +159,55 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material.Colors colors, optional androidx.wear.compose.material.Typography typography, optional androidx.wear.compose.material.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class ScalingLazyColumnDefaults {
+    method public androidx.wear.compose.material.ScalingParams scalingParams(optional float edgeScale, optional float edgeAlpha, optional float minElementHeight, optional float maxElementHeight, optional float minTransitionArea, optional float maxTransitionArea, optional androidx.compose.animation.core.Easing scaleInterpolator, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Constraints,java.lang.Integer> viewportVerticalOffsetResolver);
+    field public static final androidx.wear.compose.material.ScalingLazyColumnDefaults INSTANCE;
+  }
+
+  public final class ScalingLazyColumnKt {
+    method @androidx.compose.runtime.Composable public static void ScalingLazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.ScalingParams scalingParams, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.wear.compose.material.ScalingLazyColumnState state, kotlin.jvm.functions.Function1<? super androidx.wear.compose.material.ScalingLazyColumnScope,kotlin.Unit> content);
+  }
+
+  public final class ScalingLazyColumnMeasureKt {
+  }
+
+  public interface ScalingLazyColumnScope {
+    method public void item(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method public void items(int count, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> itemContent);
+  }
+
+  @androidx.compose.runtime.Stable public final class ScalingLazyColumnState {
+    ctor public ScalingLazyColumnState();
+    field public static final androidx.wear.compose.material.ScalingLazyColumnState.Companion Companion;
+  }
+
+  public static final class ScalingLazyColumnState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.wear.compose.material.ScalingLazyColumnState,?> getSaver();
+    property public final androidx.compose.runtime.saveable.Saver<androidx.wear.compose.material.ScalingLazyColumnState,?> Saver;
+  }
+
+  public final class ScalingLazyColumnStateKt {
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.ScalingLazyColumnState rememberScalingLazyColumnState();
+  }
+
+  public interface ScalingParams {
+    method public float getEdgeAlpha();
+    method public float getEdgeScale();
+    method public float getMaxElementHeight();
+    method public float getMaxTransitionArea();
+    method public float getMinElementHeight();
+    method public float getMinTransitionArea();
+    method public androidx.compose.animation.core.Easing getScaleInterpolator();
+    method public int resolveViewportVerticalOffset(long viewportConstraints);
+    property public abstract float edgeAlpha;
+    property public abstract float edgeScale;
+    property public abstract float maxElementHeight;
+    property public abstract float maxTransitionArea;
+    property public abstract float minElementHeight;
+    property public abstract float minTransitionArea;
+    property public abstract androidx.compose.animation.core.Easing scaleInterpolator;
+  }
+
   @androidx.compose.runtime.Immutable public final class Shapes {
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.wear.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
diff --git a/wear/compose/compose-material/samples/build.gradle b/wear/compose/compose-material/samples/build.gradle
new file mode 100644
index 0000000..748fd30
--- /dev/null
+++ b/wear/compose/compose-material/samples/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.LibraryVersions
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("AndroidXComposePlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    kotlinPlugin(project(":compose:compiler:compiler"))
+
+    implementation(libs.kotlinStdlib)
+
+    compileOnly(project(":annotation:annotation-sampled"))
+    implementation(project(":compose:foundation:foundation"))
+    implementation(project(":compose:foundation:foundation-layout"))
+    implementation(project(":compose:runtime:runtime"))
+    implementation(project(":compose:ui:ui"))
+    implementation(project(":compose:ui:ui-text"))
+    implementation(project(":wear:compose:compose-material"))
+}
+
+androidx {
+    name = "Android Wear Compose Material Samples"
+    type = LibraryType.SAMPLES
+    mavenGroup = LibraryGroups.WEAR_COMPOSE
+    inceptionYear = "2021"
+    description = "Contains the sample code for the Android Wear Compose Material Classes"
+}
diff --git a/wear/compose/compose-material/samples/src/main/AndroidManifest.xml b/wear/compose/compose-material/samples/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..67639fa
--- /dev/null
+++ b/wear/compose/compose-material/samples/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.wear.compose.material.samples">
+    <uses-sdk android:minSdkVersion="25"
+        tools:overrideLibrary="androidx.wear.compose.material"/>
+</manifest>
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ScalingLazyColumnSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ScalingLazyColumnSample.kt
new file mode 100644
index 0000000..53aea17
--- /dev/null
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ScalingLazyColumnSample.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.Chip
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.ListHeader
+import androidx.wear.compose.material.ScalingLazyColumn
+import androidx.wear.compose.material.Text
+
+@Sampled
+@Composable
+fun SimpleScalingLazyColumn() {
+    ScalingLazyColumn() {
+        items(20) {
+            Chip(
+                onClick = { },
+                label = { Text("List item $it") },
+                colors = ChipDefaults.secondaryChipColors()
+            )
+        }
+    }
+}
+
+@Composable
+fun ScalingLazyColumnWithHeaders() {
+    ScalingLazyColumn {
+        item { Spacer(modifier = Modifier.size(20.dp)) }
+        item { ListHeader { Text("Header1") } }
+        items(5) {
+            Chip(
+                onClick = { },
+                label = { Text("List item $it") },
+                colors = ChipDefaults.secondaryChipColors()
+            )
+        }
+        item { ListHeader { Text("Header2") } }
+        items(5) {
+            Chip(
+                onClick = { },
+                label = { Text("List item ${it + 5}") },
+                colors = ChipDefaults.secondaryChipColors()
+            )
+        }
+    }
+}
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt
index 798ec64..aaba692f 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Card.kt
@@ -380,7 +380,7 @@
      * image to ensure that any text drawn over the image is legible
      */
     @Composable
-    public fun imageBackgroundPainter(
+    public fun imageWithScrimBackgroundPainter(
         backgroundImagePainter: Painter,
         backgroundImageScrimBrush: Brush = Brush.linearGradient(
             colors = listOf(
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ListHeader.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ListHeader.kt
new file mode 100644
index 0000000..df7fe38
--- /dev/null
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ListHeader.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+/**
+ * A slot based composable for creating a list header item. List header items are typically expected
+ * to be text. The contents provided will have text and colors effects applied based on the
+ * MaterialTheme. The contents will be start and end padded by default and will fill the max width
+ * of the parent.
+ *
+ * Example usage:
+ * @sample androidx.wear.compose.material.samples.ScalingLazyColumnWithHeaders
+
+ * @param modifier The modifier for the list header
+ * @param contentColor The color to apply to content
+ * @param backgroundColor The background color to apply - typically Color.Tranparent
+ */
+@Composable
+public fun ListHeader(
+    modifier: Modifier = Modifier,
+    contentColor: Color = MaterialTheme.colors.onSurfaceVariant2,
+    backgroundColor: Color = Color.Transparent,
+    content: @Composable () -> Unit
+) {
+    Box(
+        modifier = modifier.height(48.dp)
+            .fillMaxWidth()
+            .wrapContentSize()
+            .background(backgroundColor)
+            .padding(horizontal = 14.dp)
+    ) {
+        CompositionLocalProvider(
+            LocalContentColor provides contentColor,
+            LocalTextStyle provides MaterialTheme.typography.button,
+            content = content
+        )
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt
new file mode 100644
index 0000000..64bfa92
--- /dev/null
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material
+
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.Easing
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import kotlin.math.roundToInt
+
+/**
+ * Receiver scope which is used by [ScalingLazyColumn].
+ */
+public interface ScalingLazyColumnScope {
+    /**
+     * Adds a single item.
+     *
+     * @param content the content of the item
+     */
+    fun item(content: @Composable () -> Unit)
+
+    /**
+     * Adds a [count] of items.
+     *
+     * @param count the items count
+     * @param itemContent the content displayed by a single item
+     */
+    fun items(count: Int, itemContent: @Composable (Int) -> Unit)
+}
+
+/**
+ * A scrolling scaling/fisheye list component that forms a key part of the Wear Material Design
+ * language. Provides scaling and transparency effects to the content items.
+ *
+ * [ScalingLazyColumn] is designed to be able to handle potentially large numbers of content
+ * items. Content items are only materialized and composed when needed.
+ *
+ * If scaling/fisheye functionality is not required then a [LazyColumn] should be considered
+ * instead to avoid any overhead of measuring and calculating scaling and transparency effects for
+ * the content items.
+ *
+ * Example usage:
+ * @sample androidx.wear.compose.material.samples.SimpleScalingLazyColumn
+ *
+ * @param modifier The modifier to be applied to the component
+ * @param scalingParams The parameters to configure the scaling and transparency effects for the
+ * component
+ * @param verticalArrangement The vertical arrangement of the layout's children. This allows us
+ * to add spacing between items and specify the arrangement of the items when we have not enough
+ * of them to fill the whole minimum size
+ * @param horizontalAlignment the horizontal alignment applied to the items
+ * @param contentPadding The padding to apply around the contents
+ * @param state The state of the component
+ */
+@Composable
+public fun ScalingLazyColumn(
+    modifier: Modifier = Modifier,
+    scalingParams: ScalingParams = ScalingLazyColumnDefaults.scalingParams(),
+    verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(4.dp),
+    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
+    contentPadding: PaddingValues = PaddingValues(horizontal = 8.dp),
+    state: ScalingLazyColumnState = rememberScalingLazyColumnState(),
+    content: ScalingLazyColumnScope.() -> Unit
+) {
+    BoxWithConstraints(modifier = modifier) {
+
+        val extraPaddingInPixels = scalingParams.resolveViewportVerticalOffset(constraints)
+        val extraPadding = with(LocalDensity.current) { extraPaddingInPixels.toDp() }
+        val combinedPaddingValues = CombinedPaddingValues(
+            contentPadding = contentPadding,
+            extraPadding = extraPadding
+        )
+        LazyColumn(
+            // TODO (b/194464849): Refactor by adding a Modifier.verticalNegativePadding fun
+            Modifier
+                .fillMaxSize()
+                .clipToBounds()
+                .layout { measurable, constraints ->
+                    require(constraints.hasBoundedWidth)
+                    require(constraints.hasBoundedHeight)
+                    val placeable = measurable.measure(
+                        Constraints.fixed(
+                            width = constraints.maxWidth,
+                            height = constraints.maxHeight +
+                                (extraPadding * 2).roundToPx()
+                        )
+                    )
+                    layout(constraints.maxWidth, constraints.maxHeight) {
+                        placeable.place(0, -extraPadding.roundToPx())
+                    }
+                },
+            horizontalAlignment = horizontalAlignment,
+            contentPadding = combinedPaddingValues,
+            verticalArrangement = verticalArrangement,
+            state = state.lazyListState
+        ) {
+            val scope = ScalingLazyColumnScopeImpl(
+                state.lazyListState,
+                this,
+                scalingParams,
+                maxHeight,
+                verticalArrangement.spacing
+            )
+            scope.content()
+        }
+    }
+}
+
+/**
+ * Contains the default values used by [ScalingLazyColumn]
+ */
+public object ScalingLazyColumnDefaults {
+    /**
+     * Creates a [ScalingParams] that represents the scaling and alpha properties for a
+     * [ScalingLazyColumn].
+     *
+     * @param edgeScale What fraction of the full size of the item to scale it by when most
+     * scaled, e.g. at the [minTransitionArea] line. A value between [0.0,1.0], so a value of 0.2f
+     * means to scale an item to 20% of its normal size.
+     *
+     * @param edgeAlpha What fraction of the full transparency of the item to scale it by when
+     * most scaled, e.g. at the [minTransitionArea] line. A value between [0.0,1.0], so a value of
+     * 0.2f means to set the alpha of an item to 20% of its normal value.
+     *
+     * @param minElementHeight The minimum element height as a ratio of the viewport size to use
+     * for determining the transition point within ([minTransitionArea], [maxTransitionArea])
+     * that a given content item will start to be scaled. Items smaller than [minElementHeight]
+     * will be treated as if [minElementHeight]. Must be less than or equal to [maxElementHeight].
+     *
+     * @param maxElementHeight The maximum element height as a ratio of the viewport size to use
+     * for determining the transition point within ([minTransitionArea], [maxTransitionArea])
+     * that a given content item will start to be scaled. Items larger than [maxElementHeight]
+     * will be treated as if [maxElementHeight]. Must be greater than or equal to
+     * [minElementHeight].
+     *
+     * @param minTransitionArea The lower bound of the scaling transition area, closest to the
+     * edge of the component. Defined as a ratio of the distance between the viewport center line
+     * and viewport edge of the list component. Must be less than or equal to [maxTransitionArea].
+     *
+     * @param maxTransitionArea The upper bound of the scaling transition area, closest to the
+     * center of the component. The is a ratio of the distance between the viewport center line and
+     * viewport edge of the list component. Must be greater
+     * than or equal to [minTransitionArea].
+     *
+     * @param scaleInterpolator An interpolator to use to determine how to apply scaling as a
+     * item transitions across the scaling transition area.
+     *
+     * @param viewportVerticalOffsetResolver The additional padding to consider above and below the
+     * viewport of a [ScalingLazyColumn] when considering which items to draw in the viewport. If
+     * set to 0 then no additional padding will be provided and only the items which would appear
+     * in the viewport before any scaling is applied will be considered for drawing, this may
+     * leave blank space at the top and bottom of the viewport where the next available item
+     * could have been drawn once other items have been scaled down in size. The larger this
+     * value is set to will allow for more content items to be considered for drawing in the
+     * viewport, however there is a performance cost associated with materializing items that are
+     * subsequently not drawn. The higher/more extreme the scaling parameters that are applied to
+     * the [ScalingLazyColumn] the more padding may be needed to ensure there are always enough
+     * content items available to be rendered. By default will be 20% of the maxHeight of the
+     * viewport above and below the content.
+     */
+    fun scalingParams(
+        edgeScale: Float = 0.5f,
+        edgeAlpha: Float = 0.5f,
+        minElementHeight: Float = 0.2f,
+        maxElementHeight: Float = 0.8f,
+        minTransitionArea: Float = 0.2f,
+        maxTransitionArea: Float = 0.6f,
+        scaleInterpolator: Easing = CubicBezierEasing(0.25f, 0.00f, 0.75f, 1.00f),
+        viewportVerticalOffsetResolver: (Constraints) -> Int = { (it.maxHeight / 5f).toInt() }
+    ): ScalingParams = DefaultScalingParams(
+        edgeScale = edgeScale,
+        edgeAlpha = edgeAlpha,
+        minElementHeight = minElementHeight,
+        maxElementHeight = maxElementHeight,
+        minTransitionArea = minTransitionArea,
+        maxTransitionArea = maxTransitionArea,
+        scaleInterpolator = scaleInterpolator,
+        viewportVerticalOffsetResolver = viewportVerticalOffsetResolver
+    )
+}
+
+private class ScalingLazyColumnScopeImpl(
+    private val state: LazyListState,
+    private val scope: LazyListScope,
+    private val scalingParams: ScalingParams,
+    private val realViewportSize: Dp,
+    private val paddingBetweenItems: Dp
+) : ScalingLazyColumnScope {
+
+    private var currentStartIndex = 0
+
+    override fun item(content: @Composable () -> Unit) {
+        val startIndex = currentStartIndex
+        scope.item {
+            ScalingLazyColumnItemWrapper(
+                startIndex,
+                state,
+                scalingParams,
+                realViewportSize,
+                paddingBetweenItems,
+                content = content
+            )
+        }
+        currentStartIndex++
+    }
+
+    override fun items(count: Int, itemContent: @Composable (Int) -> Unit) {
+        val startIndex = currentStartIndex
+        scope.items(count) {
+            ScalingLazyColumnItemWrapper(
+                startIndex + it,
+                state,
+                scalingParams,
+                realViewportSize,
+                paddingBetweenItems
+            ) {
+                itemContent(it)
+            }
+        }
+        currentStartIndex += count
+    }
+}
+
+@Composable
+private fun ScalingLazyColumnItemWrapper(
+    index: Int,
+    state: LazyListState,
+    scalingParams: ScalingParams,
+    realViewportSize: Dp,
+    paddingBetweenItems: Dp,
+    content: @Composable () -> Unit
+) {
+    Box(
+        // TODO (b/194464927): Refactor this method to make it more readable
+        Modifier.graphicsLayer {
+            val items = state.layoutInfo.visibleItemsInfo
+            val currentItem = items.find { it.index == index }
+            if (currentItem != null) {
+                val viewportSize = realViewportSize.roundToPx()
+                val centerOffset = viewportSize / 2
+                val paddingBetweenItemsPx = paddingBetweenItems.roundToPx()
+
+                val rawItemStart = currentItem.offset
+                val rawItemEnd = rawItemStart + currentItem.size
+                if (rawItemEnd < centerOffset) {
+                    var currentSumOfScaledSizeDiffs = 0
+                    items.reversed().forEach {
+                        if (it.index > currentItem.index && it.offset < centerOffset) {
+                            val (scale, _) = calculateScaleAndAlpha(
+                                0,
+                                viewportSize,
+                                it.offset + currentSumOfScaledSizeDiffs,
+                                it.offset + it.size + currentSumOfScaledSizeDiffs,
+                                scalingParams
+                            )
+                            currentSumOfScaledSizeDiffs += it.size -
+                                (it.size * scale).roundToInt() +
+                                (
+                                    paddingBetweenItemsPx -
+                                        (paddingBetweenItemsPx * scale).roundToInt()
+                                    )
+                        }
+                    }
+                    translationY = currentSumOfScaledSizeDiffs.toFloat()
+                }
+                if (rawItemEnd > centerOffset) {
+                    var currentSumOfScaledSizeDiffs = 0
+                    items.forEach {
+                        if (it.index < currentItem.index && it.offset > centerOffset) {
+                            val (scale, _) = calculateScaleAndAlpha(
+                                0,
+                                viewportSize,
+                                it.offset + currentSumOfScaledSizeDiffs,
+                                it.offset + it.size + currentSumOfScaledSizeDiffs,
+                                scalingParams
+                            )
+                            currentSumOfScaledSizeDiffs -= it.size -
+                                (it.size * scale).roundToInt() -
+                                (
+                                    paddingBetweenItemsPx -
+                                        (paddingBetweenItemsPx * scale).roundToInt()
+                                    )
+                        }
+                    }
+                    translationY = currentSumOfScaledSizeDiffs.toFloat()
+                }
+
+                val (scaleToApply, alphaToApply) = calculateScaleAndAlpha(
+                    0,
+                    viewportSize,
+                    rawItemStart + translationY.roundToInt(),
+                    rawItemEnd + translationY.roundToInt(),
+                    scalingParams
+                )
+
+                alpha = alphaToApply
+                scaleX = scaleToApply
+                scaleY = scaleToApply
+                val halfScaleSizeDiff = (currentItem.size - (currentItem.size * scaleToApply)) / 2f
+                if (rawItemEnd < centerOffset) {
+                    translationY += halfScaleSizeDiff
+                }
+                if (rawItemEnd > centerOffset) {
+                    translationY -= halfScaleSizeDiff
+                }
+            }
+        }
+    ) {
+        content()
+    }
+}
+
+@Immutable
+private class CombinedPaddingValues(
+    @Stable
+    val contentPadding: PaddingValues,
+    @Stable
+    val extraPadding: Dp
+) : PaddingValues {
+    override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp =
+        contentPadding.calculateLeftPadding(layoutDirection)
+
+    override fun calculateTopPadding(): Dp =
+        contentPadding.calculateTopPadding() + extraPadding
+
+    override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp =
+        contentPadding.calculateRightPadding(layoutDirection)
+
+    override fun calculateBottomPadding(): Dp =
+        contentPadding.calculateBottomPadding() + extraPadding
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null) return false
+        if (this::class != other::class) return false
+
+        other as CombinedPaddingValues
+
+        if (contentPadding != other.contentPadding) return false
+        if (extraPadding != other.extraPadding) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = contentPadding.hashCode()
+        result = 31 * result + extraPadding.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "CombinedPaddingValuesImpl(contentPadding=$contentPadding, " +
+            "extraPadding=$extraPadding)"
+    }
+}
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
new file mode 100644
index 0000000..401b349
--- /dev/null
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material
+
+import androidx.compose.animation.core.Easing
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.unit.Constraints
+import kotlin.math.min
+
+/**
+ * Parameters to control the scaling of the contents of a [ScalingLazyColumn]. The contents
+ * of a [ScalingLazyColumn] are scaled down (made smaller) as they move further from the center
+ * of the viewport. This scaling gives a "fisheye" effect with the contents in the center being
+ * larger and therefore more prominent.
+ *
+ * Items in the center of the component's viewport will be full sized and with normal transparency.
+ * As items move further from the center of the viewport they get smaller and become transparent.
+ *
+ * The scaling parameters allow for larger items to start being scaled closer to the center than
+ * smaller items. This allows for larger items to scale over a bigger transition area giving a more
+ * visually pleasing effect.
+ *
+ * Scaling transitions take place between a transition line and the edge of the screen. The trigger
+ * for an item to start being scaled is when its most central edge, the item's edge that is furthest
+ * from the screen edge, passing across the item's transition line. The amount of scaling to apply is
+ * a function of the how far the item has moved through its transition area. An interpolator is
+ * applied to allow for the scaling to follow a bezier curve.
+ *
+ * There are 4 properties that are used to determine an item's scaling transition point.
+ *
+ * [maxTransitionArea] and [minTransitionArea] define the range in which all item scaling lines sit.
+ * The largest items will start to scale at the [maxTransitionArea] and the smallest items will
+ * start to scale at the [minTransitionArea].
+ *
+ * The [minTransitionArea] and [maxTransitionArea] apply either side of the center line of the
+ * viewport creating 2 transition areas one at the top/start the other at the bottom/end.
+ * So a [maxTransitionArea] value of 0.6f on a Viewport of size 320 dp will give start transition
+ * line for scaling at (320 / 2) * 0.6 = 96 dp from the top/start and bottom/end edges of the
+ * viewport. Similarly [minTransitionArea] gives the point at which the scaling transition area
+ * ends, e.g. a value of 0.2 with the same 320 dp screen gives an min scaling transition area line
+ * of (320 / 2) * 0.2 = 32 dp from top/start and bottom/end. So in this example we have two
+ * transition areas in the ranges [32.dp,96.dp] and [224.dp (320.dp-96.d),288.dp (320.dp-32.dp)].
+ *
+ * Deciding for a specific content item exactly where its transition line will be within the
+ * ([minTransitionArea], [maxTransitionArea]) transition area is determined by its height as a
+ * fraction of the viewport height and the properties [minElementHeight] and [maxElementHeight],
+ * also defined as a fraction of the viewport height.
+ *
+ * If an item is smaller than [minElementHeight] it is treated as is [minElementHeight] and if
+ * larger than [maxElementHeight] then it is treated as if [maxElementHeight].
+ *
+ * Given the size of an item where it sits between [minElementHeight] and [maxElementHeight] is used
+ * to determine what fraction of the transition area to use. For example if [minElementHeight] is
+ * 0.2 and [maxElementHeight] is 0.8 then a component item that is 0.4 (40%) of the size of the
+ * viewport would start to scale when it was 0.333 (33.3%) of the way through the transition area,
+ * (0.4 - 0.2) / (0.8 - 0.2) = 0.2 / 0.6 = 0.333.
+ *
+ * Taking the example transition area above that means that the scaling line for the item would be a
+ * third of the way between 32.dp and 96.dp. 32.dp + ((96.dp-32.dp) * 0.333) = 53.dp. So this item
+ * would start to scale when it moved from the center across the 53.dp line and its scaling would be
+ * between 53.dp and 0.dp.
+ */
+public interface ScalingParams {
+    /**
+     * What fraction of the full size of the item to scale it by when most
+     * scaled, e.g. at the viewport edge. A value between [0.0,1.0], so a value of 0.2f means
+     * to scale an item to 20% of its normal size.
+     */
+//    @FloatRange(
+//        fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
+//    )
+    val edgeScale: Float
+
+    /**
+     * What fraction of the full transparency of the item to scale it by when
+     * most scaled, e.g. at the viewport edge. A value between [0.0,1.0], so a value of 0.2f
+     * means to set the alpha of an item to 20% of its normal value.
+     */
+//    @FloatRange(
+//        fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
+//    )
+    val edgeAlpha: Float
+
+    /**
+     * The minimum element height as a fraction of the viewport size to use
+     * for determining the transition point within ([minTransitionArea], [maxTransitionArea]) that a
+     * given content item will start to be scaled. Items smaller than [minElementHeight] will be
+     * treated as if [minElementHeight]. Must be less than or equal to [maxElementHeight].
+     */
+//    @FloatRange(
+//        fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
+//    )
+    val minElementHeight: Float
+
+    /**
+     * The minimum element height as a fraction of the viewport size to use
+     * for determining the transition point within ([minTransitionArea], [maxTransitionArea]) that a
+     * given content item will start to be scaled. Items smaller than [minElementHeight] will be
+     * treated as if [minElementHeight]. Must be less than or equal to [maxElementHeight].
+     */
+//    @FloatRange(
+//        fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
+//    )
+    val maxElementHeight: Float
+
+    /**
+     * The lower bound of the scaling transition area, closest to the edge
+     * of the component. Defined as a fraction of the distance between the viewport center line and
+     * viewport edge of the component. Must be less than or equal to [maxTransitionArea].
+     */
+//    @FloatRange(
+//        fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
+//    )
+    val minTransitionArea: Float
+
+    /**
+     * The upper bound of the scaling transition area, closest to the center
+     * of the component. Defined as a fraction of the distance between the viewport center line and
+     * viewport edge of the component. Must be greater
+     * than or equal to [minTransitionArea].
+     */
+//    @FloatRange(
+//        fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
+//    )
+    val maxTransitionArea: Float
+
+    /**
+     * An interpolator to use to determine how to apply scaling as a item
+     * transitions across the scaling transition area.
+     */
+    val scaleInterpolator: Easing
+
+    /**
+     * Determine the offset/extra padding (in pixels) that is used to define a space for additional
+     * items that should be considered for drawing on the screen as the scaling of the visible
+     * items in viewport is calculated. This additional padding area means that more items will
+     * materialized and therefore be in scope for being drawn in the viewport if the scaling of
+     * other elements means that there is additional space at the top and bottom of the viewport
+     * that can be used. The default value is a fifth of the viewport height allowing an
+     * additional 20% of the viewport height above and below the viewport.
+     *
+     * @param viewportConstraints the viewports constraints
+     */
+    public fun resolveViewportVerticalOffset(viewportConstraints: Constraints): Int
+}
+
+internal class DefaultScalingParams(
+    override val edgeScale: Float,
+    override val edgeAlpha: Float,
+    override val minElementHeight: Float,
+    override val maxElementHeight: Float,
+    override val minTransitionArea: Float,
+    override val maxTransitionArea: Float,
+    override val scaleInterpolator: Easing,
+    val viewportVerticalOffsetResolver: (Constraints) -> Int,
+) : ScalingParams {
+
+    init {
+        check(
+            minElementHeight <= maxElementHeight,
+            { "minElementHeight must be less than or equal to maxElementHeight" }
+        )
+        check(
+            minTransitionArea <= maxTransitionArea,
+            { "minTransitionArea must be less than or equal to maxTransitionArea" }
+        )
+    }
+
+    override fun resolveViewportVerticalOffset(viewportConstraints: Constraints): Int {
+        return viewportVerticalOffsetResolver(viewportConstraints)
+    }
+
+    override fun toString(): String {
+        return "ScalingParams(edgeScale=$edgeScale, edgeAlpha=$edgeAlpha, " +
+            "minElementHeight=$minElementHeight, maxElementHeight=$maxElementHeight, " +
+            "minTransitionArea=$minTransitionArea, maxTransitionArea=$maxTransitionArea)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null) return false
+        if (this::class != other::class) return false
+
+        other as DefaultScalingParams
+
+        if (edgeScale != other.edgeScale) return false
+        if (edgeAlpha != other.edgeAlpha) return false
+        if (minElementHeight != other.minElementHeight) return false
+        if (maxElementHeight != other.maxElementHeight) return false
+        if (minTransitionArea != other.minTransitionArea) return false
+        if (maxTransitionArea != other.maxTransitionArea) return false
+        if (scaleInterpolator != other.scaleInterpolator) return false
+        if (viewportVerticalOffsetResolver != other.viewportVerticalOffsetResolver) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = edgeScale.hashCode()
+        result = 31 * result + edgeAlpha.hashCode()
+        result = 31 * result + minElementHeight.hashCode()
+        result = 31 * result + maxElementHeight.hashCode()
+        result = 31 * result + minTransitionArea.hashCode()
+        result = 31 * result + maxTransitionArea.hashCode()
+        result = 31 * result + scaleInterpolator.hashCode()
+        result = 31 * result + viewportVerticalOffsetResolver.hashCode()
+        return result
+    }
+}
+
+/**
+ * Calculate the scale and alpha to apply for an item based on the start and end position of the
+ * component's viewport in pixels and top and bottom position of the item, also in pixels.
+ *
+ * Firstly worked out if the component is above or below the viewport's center-line which determines
+ * whether the item's top or bottom will be used as the trigger for scaling/alpha.
+ *
+ * Uses the scalingParams to determine where the scaling transition line is for the component.
+ *
+ * Then determines if the component is inside the scaling area, and if so what scaling/alpha effects
+ * to apply.
+ *
+ * @param viewPortStartPx The start position of the component's viewport in pixels
+ * @param viewPortEndPx The end position of the component's viewport in pixels
+ * @param itemTopPx The top of the content item in pixels.
+ * @param itemBottomPx The bottom of the content item in pixels.
+ * @param scalingParams The parameters that determine where the item's scaling transition line
+ * is, how scaling and transparency to apply.
+ */
+internal fun calculateScaleAndAlpha(
+    viewPortStartPx: Int,
+    viewPortEndPx: Int,
+    itemTopPx: Int,
+    itemBottomPx: Int,
+    scalingParams: ScalingParams,
+): ScaleAndAlpha {
+    var scaleToApply = 1.0f
+    var alphaToApply = 1.0f
+
+    val viewPortHeightPx = (viewPortEndPx - viewPortStartPx).toFloat()
+    val itemHeightPx = (itemBottomPx - itemTopPx).toFloat()
+
+    /*
+     * Calculate the position of the edge of the item closest to the center line of the viewport as
+     * a fraction of the viewport. The [itemEdgePx] and [scrollPositionPx] values are in pixels.
+     */
+    val itemEdgeAsFractionOfViewport =
+        min(itemBottomPx - viewPortStartPx, viewPortEndPx - itemTopPx) / viewPortHeightPx
+
+    val heightAsFractionOfViewPort = itemHeightPx / viewPortHeightPx
+    if (itemEdgeAsFractionOfViewport > 0.0f && itemEdgeAsFractionOfViewport < 1.0f) {
+        // Work out the scaling line based on size, this is a value between 0.0..1.0
+        val sizeRatio: Float =
+            (
+                (heightAsFractionOfViewPort - scalingParams.minElementHeight) /
+                    (scalingParams.maxElementHeight - scalingParams.minElementHeight)
+                ).coerceIn(0f, 1f)
+
+        val scalingLineAsFractionOfViewPort =
+            scalingParams.minTransitionArea +
+                (scalingParams.maxTransitionArea - scalingParams.minTransitionArea) *
+                sizeRatio
+
+        if (itemEdgeAsFractionOfViewport < scalingLineAsFractionOfViewPort) {
+            // We are scaling
+            val fractionOfDiffToApplyRaw =
+                (scalingLineAsFractionOfViewPort - itemEdgeAsFractionOfViewport) /
+                    scalingLineAsFractionOfViewPort
+            val fractionOfDiffToApplyInterpolated =
+                scalingParams.scaleInterpolator.transform(fractionOfDiffToApplyRaw)
+
+            scaleToApply =
+                scalingParams.edgeScale +
+                (1.0f - scalingParams.edgeScale) *
+                (1.0f - fractionOfDiffToApplyInterpolated)
+            alphaToApply =
+                scalingParams.edgeAlpha +
+                (1.0f - scalingParams.edgeAlpha) *
+                (1.0f - fractionOfDiffToApplyInterpolated)
+        }
+    } else {
+        scaleToApply = scalingParams.edgeScale
+        alphaToApply = scalingParams.edgeAlpha
+    }
+
+    return ScaleAndAlpha(scaleToApply, alphaToApply)
+}
+
+@Immutable
+internal data class ScaleAndAlpha(
+    val scale: Float,
+    val alpha: Float
+)
+
+@Immutable
+internal data class ScaleAlphaAndStartPosition(val scale: Float, val alpha: Float, val top: Int)
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnState.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnState.kt
new file mode 100644
index 0000000..738ed4d
--- /dev/null
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnState.kt
@@ -0,0 +1,52 @@
+package androidx.wear.compose.material
+
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.saveable.Saver
+import androidx.compose.runtime.saveable.listSaver
+import androidx.compose.runtime.saveable.rememberSaveable
+
+/**
+ * Creates a [ScalingLazyColumnState] that is remembered across compositions.
+ */
+@Composable
+public fun rememberScalingLazyColumnState(): ScalingLazyColumnState {
+    return rememberSaveable(saver = ScalingLazyColumnState.Saver) {
+        ScalingLazyColumnState()
+    }
+}
+
+/**
+ * A state object that can be hoisted to control and observe scrolling.
+ * TODO (b/193792848): Add scrolling and snap support.
+ *
+ * In most cases, this will be created via [rememberScalingLazyColumnState].
+ */
+@Stable
+public class ScalingLazyColumnState {
+
+    internal var lazyListState: LazyListState = LazyListState(0, 0)
+
+    companion object {
+        /**
+         * The default [Saver] implementation for [ScalingLazyColumnState].
+         */
+        val Saver: Saver<ScalingLazyColumnState, *> = listSaver(
+            save = {
+                listOf(
+                    it.lazyListState.firstVisibleItemIndex,
+                    it.lazyListState.firstVisibleItemScrollOffset
+                )
+            },
+            restore = {
+                var scalingLazyColumnState = ScalingLazyColumnState()
+                scalingLazyColumnState.lazyListState = LazyListState(
+                    firstVisibleItemIndex = it[0],
+                    firstVisibleItemScrollOffset = it[1]
+                )
+                scalingLazyColumnState
+            }
+        )
+    }
+}
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index 84713d9..6ddb2f4 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -57,6 +57,7 @@
     implementation(project(':wear:compose:compose-material'))
     implementation(project(':wear:compose:compose-foundation'))
     implementation(project(":wear:compose:compose-foundation-samples"))
+    implementation(project(":wear:compose:compose-material-samples"))
 
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
     androidTestImplementation(libs.testCore)
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt
index a480cb27..b6c6260 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt
@@ -130,7 +130,7 @@
                     Text("Text coloured to stand out on the image")
                 }
             },
-            backgroundPainter = CardDefaults.imageBackgroundPainter(
+            backgroundPainter = CardDefaults.imageWithScrimBackgroundPainter(
                 backgroundImagePainter = painterResource(id = R.drawable.backgroundimage1)
             ),
             bodyColor = MaterialTheme.colors.onSurface,
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
index 7f313ed..798cb59 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
@@ -18,6 +18,8 @@
 
 import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.integration.demos.common.DemoCategory
+import androidx.wear.compose.material.samples.ScalingLazyColumnWithHeaders
+import androidx.wear.compose.material.samples.SimpleScalingLazyColumn
 
 val WearMaterialDemos = DemoCategory(
     "Material",
@@ -49,5 +51,7 @@
             )
         ),
         ComposableDemo("Card") { CardDemo() },
+        ComposableDemo("Scaling Lazy Column") { SimpleScalingLazyColumn() },
+        ComposableDemo("List Headers") { ScalingLazyColumnWithHeaders() },
     ),
 )
diff --git a/wear/wear-phone-interactions/build.gradle b/wear/wear-phone-interactions/build.gradle
index 4b02455..77907c0 100644
--- a/wear/wear-phone-interactions/build.gradle
+++ b/wear/wear-phone-interactions/build.gradle
@@ -38,6 +38,7 @@
     testImplementation(libs.robolectric)
     testImplementation(libs.mockitoCore)
     testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.truth)
 }
 
 android {
diff --git a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/OAuthRequest.kt b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/OAuthRequest.kt
index 7426ffc..2590363 100644
--- a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/OAuthRequest.kt
+++ b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/OAuthRequest.kt
@@ -148,10 +148,11 @@
                 "redirect_uri",
                 Uri.withAppendedPath(
                     if (redirectUrl == null) {
-                        if (WearTypeHelper.isChinaBuild(context))
+                        if (WearTypeHelper.isChinaBuild(context)) {
                             Uri.parse(WEAR_REDIRECT_URL_PREFIX_CN)
-                        else
+                        } else {
                             Uri.parse(WEAR_REDIRECT_URL_PREFIX)
+                        }
                     } else {
                         redirectUrl
                     },
@@ -178,14 +179,14 @@
             expectedQueryParam: String
         ) {
             val currentQueryParam = authProviderUrl!!.getQueryParameter(queryKey)
-            currentQueryParam?.let {
+            if (currentQueryParam != null) {
                 require(expectedQueryParam == currentQueryParam) {
                     "The '$queryKey' query param already exists in the authProviderUrl, " +
                         "expect to have the value of '$expectedQueryParam', but " +
                         "'$currentQueryParam' is given. Please correct it,  or leave it out " +
                         "to allow the request builder to append it automatically."
                 }
-            } ?: run {
+            } else {
                 requestUriBuilder.appendQueryParameter(queryKey, expectedQueryParam)
             }
         }
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt
index 130e3a8..093ca7c 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt
@@ -23,7 +23,7 @@
 import android.net.Uri
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.phone.interactions.PhoneTypeHelper.Companion.getPhoneDeviceType
-import org.junit.Assert.assertEquals
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,36 +61,32 @@
     @Test
     fun testGetDeviceType_returnsIosWhenAltMode() {
         createFakePhoneTypeQuery(PhoneTypeHelper.IOS_MODE)
-        assertEquals(
-            getPhoneDeviceType(ApplicationProvider.getApplicationContext()).toLong(),
-            PhoneTypeHelper.DEVICE_TYPE_IOS.toLong()
-        )
+        assertThat(
+            getPhoneDeviceType(ApplicationProvider.getApplicationContext())
+        ).isEqualTo(PhoneTypeHelper.DEVICE_TYPE_IOS)
     }
 
     @Test
     fun testGetDeviceType_returnsAndroidWhenNonAltMode() {
         createFakePhoneTypeQuery(PhoneTypeHelper.ANDROID_MODE)
-        assertEquals(
-            getPhoneDeviceType(ApplicationProvider.getApplicationContext()).toLong(),
-            PhoneTypeHelper.DEVICE_TYPE_ANDROID.toLong()
-        )
+        assertThat(
+            getPhoneDeviceType(ApplicationProvider.getApplicationContext())
+        ).isEqualTo(PhoneTypeHelper.DEVICE_TYPE_ANDROID)
     }
 
     @Test
     fun testGetDeviceType_returnsErrorWhenModeUnknown() {
         createFakePhoneTypeQuery(PhoneTypeHelper.DEVICE_TYPE_UNKNOWN)
-        assertEquals(
-            getPhoneDeviceType(ApplicationProvider.getApplicationContext()).toLong(),
-            PhoneTypeHelper.DEVICE_TYPE_UNKNOWN.toLong()
-        )
+        assertThat(
+            getPhoneDeviceType(ApplicationProvider.getApplicationContext())
+        ).isEqualTo(PhoneTypeHelper.DEVICE_TYPE_UNKNOWN)
     }
 
     @Test
     fun testGetDeviceType_returnsErrorWhenContentMissing() {
-        assertEquals(
-            getPhoneDeviceType(ApplicationProvider.getApplicationContext()).toLong(),
-            PhoneTypeHelper.DEVICE_TYPE_ERROR.toLong()
-        )
+        assertThat(
+            getPhoneDeviceType(ApplicationProvider.getApplicationContext())
+        ).isEqualTo(PhoneTypeHelper.DEVICE_TYPE_ERROR)
     }
 
     /*
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/CodeVerifierCodeChallengeTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/CodeVerifierCodeChallengeTest.kt
index e92e749..9de98e3 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/CodeVerifierCodeChallengeTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/CodeVerifierCodeChallengeTest.kt
@@ -16,24 +16,27 @@
 
 package androidx.wear.phone.interactions.authentication
 
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
+import android.os.Build
+import androidx.annotation.RequiresApi
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.fail
 import org.junit.Test
+import org.robolectric.annotation.Config
 
 /** Unit tests for [CodeVerifier] and [CodeChallenge] */
+@Config(minSdk = 26)
+@RequiresApi(Build.VERSION_CODES.O)
 public class CodeVerifierCodeChallengeTest {
     @Test
     public fun testVerifierDefaultConstructor() {
         val verifier = CodeVerifier()
-        assertEquals(43, verifier.value.length)
+        assertThat(verifier.value.length).isEqualTo(43)
     }
 
     @Test
     public fun testVerifierConstructor() {
         val verifier = CodeVerifier(96)
-        assertEquals(128, verifier.value.length)
+        assertThat(verifier.value.length).isEqualTo(128)
     }
 
     @Test
@@ -49,14 +52,14 @@
     @Test
     public fun testVerifierEquality() {
         val verifier = CodeVerifier()
-        assertTrue(verifier.equals(CodeVerifier(verifier.value)))
+        assertThat(CodeVerifier(verifier.value)).isEqualTo(verifier)
     }
 
     @Test
     public fun testVerifierInequality() {
-        assertFalse(CodeVerifier().equals(CodeVerifier()))
-        assertFalse(CodeVerifier(50).equals(CodeVerifier(50)))
-        assertFalse(CodeVerifier().equals(null))
+        assertThat(CodeVerifier()).isNotEqualTo(CodeVerifier())
+        assertThat(CodeVerifier(50)).isNotEqualTo(CodeVerifier(50))
+        assertThat(CodeVerifier()).isNotEqualTo(null)
     }
 
     @Test
@@ -64,22 +67,20 @@
         // see https://tools.ietf.org/html/rfc7636#appendix-A
         val verifier = CodeVerifier("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")
         val challenge = CodeChallenge(verifier)
-        assertEquals("E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", challenge.value)
+        assertThat(challenge.value).isEqualTo("E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM")
     }
 
     @Test
     public fun testChallengeEquality() {
         val verifierValue = "jdshfkshg-8973834_SDFSSGE"
-        assertTrue(
-            CodeChallenge(CodeVerifier(verifierValue)).equals(
-                CodeChallenge(CodeVerifier(verifierValue))
-            )
-        )
+        assertThat(
+            CodeChallenge(CodeVerifier(verifierValue))
+        ).isEqualTo(CodeChallenge(CodeVerifier(verifierValue)))
     }
 
     @Test
     public fun testChallengeInequality() {
-        assertFalse(CodeChallenge(CodeVerifier()).equals(CodeChallenge(CodeVerifier())))
-        assertFalse(CodeChallenge(CodeVerifier()).equals(null))
+        assertThat(CodeChallenge(CodeVerifier())).isNotEqualTo(CodeChallenge(CodeVerifier()))
+        assertThat(CodeChallenge(CodeVerifier())).isNotEqualTo(null)
     }
 }
\ No newline at end of file
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/OAuthRequestResponseTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/OAuthRequestResponseTest.kt
index 8a2701c..3568a44 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/OAuthRequestResponseTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/OAuthRequestResponseTest.kt
@@ -22,8 +22,7 @@
 import androidx.annotation.RequiresApi
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.phone.interactions.WearPhoneInteractionsTestRunner
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Test
@@ -58,7 +57,7 @@
     fun setUp() {
         // We need to make sure that context.packageName is not empty as it can lead to passing
         // tests when they shouldn't.
-        assertFalse(context.packageName.isEmpty())
+        assertThat(context.packageName).isNotEmpty()
     }
 
     private fun setSystemFeatureChina(value: Boolean) {
@@ -80,12 +79,12 @@
         }
 
         val requestUrl = request!!.requestUrl
-        assertEquals(requestUrl.toString().indexOf(expectedAuthProviderUrl), 0)
-        assertEquals(requestUrl.getQueryParameter("redirect_uri"), expectedRedirectUri)
-        assertEquals(requestUrl.getQueryParameter("client_id"), expectedClientId)
-        assertEquals(requestUrl.getQueryParameter("response_type"), "code")
-        assertEquals(requestUrl.getQueryParameter("code_challenge"), expectedCodeChallenge)
-        assertEquals(requestUrl.getQueryParameter("code_challenge_method"), "S256")
+        assertThat(requestUrl.toString().indexOf(expectedAuthProviderUrl)).isEqualTo(0)
+        assertThat(requestUrl.getQueryParameter("redirect_uri")).isEqualTo(expectedRedirectUri)
+        assertThat(requestUrl.getQueryParameter("client_id")).isEqualTo(expectedClientId)
+        assertThat(requestUrl.getQueryParameter("response_type")).isEqualTo("code")
+        assertThat(requestUrl.getQueryParameter("code_challenge")).isEqualTo(expectedCodeChallenge)
+        assertThat(requestUrl.getQueryParameter("code_challenge_method")).isEqualTo("S256")
     }
 
     private fun checkBuildFailure(builder: OAuthRequest.Builder, errorMsg: String) {
@@ -93,7 +92,7 @@
             builder.build()
             fail("should fail without providing correct/adequate info for building request")
         } catch (e: Exception) {
-            assertEquals(errorMsg, e.message)
+            assertThat(e.message).isEqualTo(errorMsg)
         }
     }
 
@@ -418,8 +417,8 @@
     public fun testNoErrorResponseBuild() {
         val response = OAuthResponse.Builder().setResponseUrl(responseUrl).build()
 
-        assertEquals(RemoteAuthClient.NO_ERROR, response.errorCode)
-        assertEquals(responseUrl, response.responseUrl)
+        assertThat(response.errorCode).isEqualTo(RemoteAuthClient.NO_ERROR)
+        assertThat(response.responseUrl).isEqualTo(responseUrl)
     }
 
     @Test
@@ -428,14 +427,14 @@
             .setErrorCode(RemoteAuthClient.ERROR_UNSUPPORTED)
             .build()
 
-        assertEquals(RemoteAuthClient.ERROR_UNSUPPORTED, response1.errorCode)
-        assertEquals(null, response1.responseUrl)
+        assertThat(response1.errorCode).isEqualTo(RemoteAuthClient.ERROR_UNSUPPORTED)
+        assertThat(response1.responseUrl).isNull()
 
         val response2 = OAuthResponse.Builder()
             .setErrorCode(RemoteAuthClient.ERROR_PHONE_UNAVAILABLE)
             .build()
 
-        assertEquals(RemoteAuthClient.ERROR_PHONE_UNAVAILABLE, response2.errorCode)
-        assertEquals(null, response2.responseUrl)
+        assertThat(response2.errorCode).isEqualTo(RemoteAuthClient.ERROR_PHONE_UNAVAILABLE)
+        assertThat(response2.responseUrl).isNull()
     }
 }
\ No newline at end of file
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt
index ed0b6dc..2d0b389 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt
@@ -21,25 +21,31 @@
 import android.content.Intent
 import android.content.ServiceConnection
 import android.net.Uri
+import android.os.Build
 import android.os.IBinder
 import android.os.RemoteException
 import android.util.Pair
+import androidx.annotation.RequiresApi
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.phone.interactions.WearPhoneInteractionsTestRunner
-import org.junit.Assert
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito
 import org.robolectric.Shadows
+import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
-import java.util.ArrayList
 import java.util.concurrent.Executor
 
 /** Unit tests for [RemoteAuthClient].  */
 @RunWith(WearPhoneInteractionsTestRunner::class)
 @DoNotInstrument // Needed because it is defined in the "android" package.
+@Config(minSdk = 26)
+@RequiresApi(Build.VERSION_CODES.O)
 public class RemoteAuthTest {
 
+    @Config(minSdk = 26)
+    @RequiresApi(Build.VERSION_CODES.O)
     internal companion object {
         private val context: Context = ApplicationProvider.getApplicationContext()
         private val shadowPackageManager = Shadows.shadowOf(context.packageManager)
@@ -67,6 +73,7 @@
                     .setCodeChallenge(CodeChallenge(CodeVerifier()))
                     .build()
         }
+
         private val response = OAuthResponse.Builder().setResponseUrl(responseUrl).build()
 
         // Note: This can't be static as Robolectric isn't set up at class init time.
@@ -91,7 +98,7 @@
     public fun doesntConnectUntilARequestIsMade() {
         // WHEN the client is created
         // THEN the Auth library should not yet connect to Clockwork Home
-        Assert.assertEquals(ConnectionState.DISCONNECTED, fakeServiceBinder.state)
+        assertThat(fakeServiceBinder.state).isEqualTo(ConnectionState.DISCONNECTED)
     }
 
     @Test
@@ -108,7 +115,7 @@
             mockCallback
         )
         // THEN a connection is made to Clockwork Home's Auth service
-        Assert.assertEquals(ConnectionState.CONNECTING, fakeServiceBinder.state)
+        assertThat(fakeServiceBinder.state).isEqualTo(ConnectionState.CONNECTING)
     }
 
     @Test
@@ -120,14 +127,8 @@
         val request = fakeService.requests[0]
         val requestReceived = request.first
         // THEN the request url is set correctly
-        Assert.assertEquals(
-            requestA.requestUrl,
-            requestReceived.requestUrl
-        )
-        Assert.assertEquals(
-            requestReceived.requestUrl.toString().indexOf(authProviderUrlA),
-            0
-        )
+        assertThat(requestReceived.requestUrl).isEqualTo(requestA.requestUrl)
+        assertThat(requestReceived.requestUrl.toString().indexOf(authProviderUrlA)).isEqualTo(0)
     }
 
     @Test
@@ -140,24 +141,12 @@
         // THEN two requests are made to Clockwork Home
         val requestAReceived = fakeService.requests[0].first
         val requestBReceived = fakeService.requests[1].first
-        Assert.assertEquals(2, fakeService.requests.size.toLong())
+        assertThat(fakeService.requests.size.toLong()).isEqualTo(2)
         // THEN the request url is set correctly for both (A then B)
-        Assert.assertEquals(
-            requestA.requestUrl,
-            requestAReceived.requestUrl
-        )
-        Assert.assertEquals(
-            requestB.requestUrl,
-            requestBReceived.requestUrl
-        )
-        Assert.assertEquals(
-            requestAReceived.requestUrl.toString().indexOf(authProviderUrlA),
-            0
-        )
-        Assert.assertEquals(
-            requestBReceived.requestUrl.toString().indexOf(authProviderUrlB),
-            0
-        )
+        assertThat(requestAReceived.requestUrl).isEqualTo(requestA.requestUrl)
+        assertThat(requestBReceived.requestUrl).isEqualTo(requestB.requestUrl)
+        assertThat(requestAReceived.requestUrl.toString().indexOf(authProviderUrlA)).isEqualTo(0)
+        assertThat(requestBReceived.requestUrl.toString().indexOf(authProviderUrlB)).isEqualTo(0)
     }
 
     @Test
@@ -187,7 +176,7 @@
         )
         // THEN the service remains connected (as there's still a request ongoing, and we won't get
         // the callback for the other request if we unbind now)
-        Assert.assertEquals(ConnectionState.CONNECTED, fakeServiceBinder.state)
+        assertThat(fakeServiceBinder.state).isEqualTo(ConnectionState.CONNECTED)
     }
 
     @Test
@@ -208,10 +197,10 @@
             fakeService.requests[1].second
         )
         // THEN the OAuth library disconnects from Clockwork Home
-        Assert.assertEquals(ConnectionState.DISCONNECTED, fakeServiceBinder.state)
+        assertThat(fakeServiceBinder.state).isEqualTo(ConnectionState.DISCONNECTED)
     }
 
-    private enum class ConnectionState {
+    internal enum class ConnectionState {
         DISCONNECTED, CONNECTING, CONNECTED
     }
 
@@ -237,7 +226,7 @@
         }
 
         fun completeConnection() {
-            Assert.assertTrue(state == ConnectionState.CONNECTING)
+            assertThat(ConnectionState.CONNECTING).isEqualTo(state)
             state = ConnectionState.CONNECTED
             serviceConnection!!.onServiceConnected(mServiceName, fakeService.onBind(Intent()))
         }
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerServiceTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerServiceTest.kt
index 7100b14..4c765e2 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerServiceTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerServiceTest.kt
@@ -21,9 +21,7 @@
 import android.support.wearable.notifications.IBridgingManagerService
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.phone.interactions.WearPhoneInteractionsTestRunner
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotNull
-import org.junit.Assert.assertNull
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Test
@@ -60,12 +58,12 @@
 
         val binder = bridgingManagerService.onBind(intent)
 
-        assertNotNull(binder)
+        assertThat(binder).isNotNull()
         val bridgingManagerServiceImpl = IBridgingManagerService.Stub.asInterface(binder)
-        assertNotNull(bridgingManagerServiceImpl)
+        assertThat(bridgingManagerServiceImpl).isNotNull()
 
         bridgingManagerServiceImpl.setBridgingConfig(bridgingConfig.toBundle(context))
-        assertEquals(bridgingConfig, testBridgingConfigurationHandler.bridgingConfig)
+        assertThat(testBridgingConfigurationHandler.bridgingConfig).isEqualTo(bridgingConfig)
     }
 
     @Test
@@ -79,11 +77,11 @@
             context /* packageName = PACKAGE_NAME */, false
         ).build()
 
-        var binder = bridgingManagerService.onBind(intent)
+        val binder = bridgingManagerService.onBind(intent)
 
-        assertNotNull(binder)
+        assertThat(binder).isNotNull()
         val bridgingManagerServiceImpl = IBridgingManagerService.Stub.asInterface(binder)
-        assertNotNull(bridgingManagerServiceImpl)
+        assertThat(bridgingManagerServiceImpl).isNotNull()
 
         assertThrows(
             IllegalArgumentException::class.java
@@ -100,7 +98,7 @@
 
         val binder = bridgingManagerService.onBind(intent)
 
-        assertNull(binder)
+        assertThat(binder).isNull()
     }
 
     @Test
@@ -110,7 +108,7 @@
 
         val binder = bridgingManagerService.onBind(intent = null)
 
-        assertNull(binder)
+        assertThat(binder).isNull()
     }
 
     companion object {
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerTest.kt
index 8d2ab49..7946e50 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/notifications/BridgingManagerTest.kt
@@ -19,14 +19,13 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import androidx.wear.phone.interactions.WearPhoneInteractionsTestRunner
-import org.junit.Assert
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.mock
 import org.robolectric.annotation.internal.DoNotInstrument
 import java.util.Arrays
-import java.util.HashSet
 
 /** Unit tests for [BridgingManager].  */
 @RunWith(WearPhoneInteractionsTestRunner::class)
@@ -48,12 +47,12 @@
             mContext, false
         ).build()
 
-        Assert.assertEquals(BridgingConfig(PACKAGE_NAME, false, HashSet()), bridgingConfig)
+        assertThat(bridgingConfig).isEqualTo(BridgingConfig(PACKAGE_NAME, false, HashSet()))
 
         // Test that conversion to and from bundle works as expected.
-        Assert.assertEquals(
-            bridgingConfig, BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
-        )
+        assertThat(
+            BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
+        ).isEqualTo(bridgingConfig)
     }
 
     @Test
@@ -62,12 +61,12 @@
             mContext, true
         ).build()
 
-        Assert.assertEquals(BridgingConfig(PACKAGE_NAME, true, HashSet()), bridgingConfig)
+        assertThat(bridgingConfig).isEqualTo(BridgingConfig(PACKAGE_NAME, true, HashSet()))
 
         // Test that conversion to and from bundle works as expected.
-        Assert.assertEquals(
-            bridgingConfig, BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
-        )
+        assertThat(
+            BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
+        ).isEqualTo(bridgingConfig)
     }
 
     @Test
@@ -76,12 +75,12 @@
             mContext, true
         ).build()
 
-        Assert.assertTrue(BridgingConfig(PACKAGE_NAME, true, HashSet()).equals(bridgingConfig))
+        assertThat(bridgingConfig).isEqualTo(BridgingConfig(PACKAGE_NAME, true, HashSet()))
 
         // Test that conversion to and from bundle works as expected.
-        Assert.assertEquals(
-            bridgingConfig, BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
-        )
+        assertThat(
+            BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
+        ).isEqualTo(bridgingConfig)
     }
 
     @Test
@@ -90,15 +89,14 @@
             mContext, true
         ).addExcludedTag("foo").build()
 
-        Assert.assertEquals(
-            BridgingConfig(PACKAGE_NAME, true, HashSet(listOf("foo"))),
+        assertThat(
             bridgingConfig
-        )
+        ).isEqualTo(BridgingConfig(PACKAGE_NAME, true, HashSet(listOf("foo"))))
 
         // Test that conversion to and from bundle works as expected.
-        Assert.assertEquals(
-            bridgingConfig, BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
-        )
+        assertThat(
+            BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
+        ).isEqualTo(bridgingConfig)
     }
 
     @Test
@@ -111,15 +109,14 @@
             .addExcludedTag("foo")
             .build()
 
-        Assert.assertEquals(
-            BridgingConfig(PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar"))),
+        assertThat(
             bridgingConfig
-        )
+        ).isEqualTo(BridgingConfig(PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar"))))
 
         // Test that conversion to and from bundle works as expected.
-        Assert.assertEquals(
-            bridgingConfig, BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
-        )
+        assertThat(
+            BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
+        ).isEqualTo(bridgingConfig)
     }
 
     @Test
@@ -130,15 +127,14 @@
             .addExcludedTags(Arrays.asList("foo", "bar", "foo"))
             .build()
 
-        Assert.assertEquals(
-            BridgingConfig(PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar"))),
+        assertThat(
             bridgingConfig
-        )
+        ).isEqualTo(BridgingConfig(PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar"))))
 
         // Test that conversion to and from bundle works as expected.
-        Assert.assertEquals(
-            bridgingConfig, BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
-        )
+        assertThat(
+            BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
+        ).isEqualTo(bridgingConfig)
     }
 
     @Test
@@ -153,17 +149,18 @@
             .addExcludedTag("foo")
             .build()
 
-        Assert.assertEquals(
+        assertThat(
+            bridgingConfig
+        ).isEqualTo(
             BridgingConfig(
                 PACKAGE_NAME, false, HashSet(Arrays.asList("foo", "bar", "123", "aaa", "abc"))
-            ),
-            bridgingConfig
+            )
         )
 
         // Test that conversion to and from bundle works as expected.
-        Assert.assertEquals(
-            bridgingConfig, BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
-        )
+        assertThat(
+            BridgingConfig.fromBundle(bridgingConfig.toBundle(mContext))
+        ).isEqualTo(bridgingConfig)
     }
 
     private companion object {
diff --git a/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt b/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
index feddeee..576ad34 100644
--- a/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
+++ b/wear/wear-watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
@@ -136,7 +136,7 @@
 
 internal val redStyleOption = ListOption(Option.Id("red_style"), "Red", icon = null)
 internal val greenStyleOption = ListOption(Option.Id("green_style"), "Green", icon = null)
-internal val blueStyleOption = ListOption(Option.Id("bluestyle"), "Blue", icon = null)
+internal val blueStyleOption = ListOption(Option.Id("blue_style"), "Blue", icon = null)
 internal val colorStyleList = listOf(redStyleOption, greenStyleOption, blueStyleOption)
 internal val colorStyleSetting = UserStyleSetting.ListUserStyleSetting(
     UserStyleSetting.Id("color_style_setting"),
diff --git a/wear/wear-watchface-samples-minimal-complications/src/main/java/androidx/wear/watchface/samples/minimal/complications/ConfigActivity.java b/wear/wear-watchface-samples-minimal-complications/src/main/java/androidx/wear/watchface/samples/minimal/complications/ConfigActivity.java
index 3892a05..aeef072 100644
--- a/wear/wear-watchface-samples-minimal-complications/src/main/java/androidx/wear/watchface/samples/minimal/complications/ConfigActivity.java
+++ b/wear/wear-watchface-samples-minimal-complications/src/main/java/androidx/wear/watchface/samples/minimal/complications/ConfigActivity.java
@@ -16,11 +16,8 @@
 
 package androidx.wear.watchface.samples.minimal.complications;
 
-import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -29,6 +26,7 @@
 import androidx.activity.ComponentActivity;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.core.content.ContextCompat;
 import androidx.wear.complications.ComplicationDataSourceInfo;
 import androidx.wear.complications.data.ComplicationData;
 import androidx.wear.watchface.complications.rendering.ComplicationDrawable;
@@ -56,11 +54,7 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.config_activity_layout);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            mMainExecutor = MainExecutorApi28.getMainExecutor(this);
-        } else {
-            mMainExecutor = MainExecutorApi25.getMainExecutor();
-        }
+        mMainExecutor = ContextCompat.getMainExecutor(getApplicationContext());
 
         mComplicationProviderName = findViewById(R.id.complication_provider_name);
         mComplicationPreview = findViewById(R.id.complication_preview);
@@ -177,19 +171,4 @@
     private <T> void addCallback(ListenableFuture<T> future, FutureCallback<T> callback) {
         FutureCallback.addCallback(future, callback, mMainExecutor);
     }
-
-    private static final class MainExecutorApi25 {
-        public static Handler sMainThreadHandler = new Handler(Looper.getMainLooper());
-
-        public static Executor getMainExecutor() {
-            return sMainThreadHandler::post;
-        }
-    }
-
-    @RequiresApi(Build.VERSION_CODES.P)
-    private static final class MainExecutorApi28 {
-        public static Executor getMainExecutor(Context context) {
-            return context.getMainExecutor();
-        }
-    }
 }
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/ConfigActivity.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/ConfigActivity.java
index 8aff71d..2241a0c 100644
--- a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/ConfigActivity.java
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/ConfigActivity.java
@@ -16,17 +16,13 @@
 
 package androidx.wear.watchface.samples.minimal.style;
 
-import android.content.Context;
-import android.os.Build;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import android.widget.TextView;
 
 import androidx.activity.ComponentActivity;
 import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
+import androidx.core.content.ContextCompat;
 import androidx.wear.watchface.editor.ListenableEditorSession;
 import androidx.wear.watchface.style.UserStyle;
 import androidx.wear.watchface.style.UserStyleSetting;
@@ -58,11 +54,7 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.config_activity_layout);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            mMainExecutor = MainExecutorApi28.getMainExecutor(this);
-        } else {
-            mMainExecutor = MainExecutorApi25.getMainExecutor();
-        }
+        mMainExecutor = ContextCompat.getMainExecutor(getApplicationContext());
         mTimeStyle = new TimeStyle(this);
 
         mStyleValue = findViewById(R.id.style_value);
@@ -136,19 +128,4 @@
         map.put(TimeStyle.Value.SECONDS, TimeStyle.Value.MINIMAL);
         return map;
     }
-
-    private static final class MainExecutorApi25 {
-        public static Handler sMainThreadHandler = new Handler(Looper.getMainLooper());
-
-        public static Executor getMainExecutor() {
-            return sMainThreadHandler::post;
-        }
-    }
-
-    @RequiresApi(Build.VERSION_CODES.P)
-    private static final class MainExecutorApi28 {
-        public static Executor getMainExecutor(Context context) {
-            return context.getMainExecutor();
-        }
-    }
 }
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/TimeStyle.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/TimeStyle.java
index 9883bf3..db60362 100644
--- a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/TimeStyle.java
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/TimeStyle.java
@@ -37,7 +37,7 @@
         SECONDS,
     }
 
-    private static final UserStyleSetting.Id ID = new UserStyleSetting.Id("TypeStyle");
+    private static final UserStyleSetting.Id ID = new UserStyleSetting.Id("TimeStyle");
 
     private static final UserStyleSetting.Option.Id MINIMAL_ID =
             new UserStyleSetting.Option.Id("minimal");
@@ -102,12 +102,12 @@
                         new UserStyleSetting.ListUserStyleSetting.ListOption(
                                 MINIMAL_ID,
                                 getString(context, R.string.time_style_minimal_name),
-                                getIcon(context, R.drawable.tyme_style_minimal_icon)
+                                getIcon(context, R.drawable.time_style_minimal_icon)
                         ),
                         new UserStyleSetting.ListUserStyleSetting.ListOption(
                                 SECONDS_ID,
                                 getString(context, R.string.time_style_seconds_name),
-                                getIcon(context, R.drawable.tyme_style_seconds_icon)
+                                getIcon(context, R.drawable.time_style_seconds_icon)
                         )
                 ),
                 WatchFaceLayer.ALL_WATCH_FACE_LAYERS);
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_minimal_icon.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/time_style_minimal_icon.xml
similarity index 100%
rename from wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_minimal_icon.xml
rename to wear/wear-watchface-samples-minimal-style/src/main/res/drawable/time_style_minimal_icon.xml
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_seconds_icon.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/time_style_seconds_icon.xml
similarity index 100%
rename from wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_seconds_icon.xml
rename to wear/wear-watchface-samples-minimal-style/src/main/res/drawable/time_style_seconds_icon.xml
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 bb4a276..204bfa0 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
@@ -38,7 +38,7 @@
         ListUserStyleSetting.ListOption(Option.Id("green_style"), "Green", icon = null)
 
     private val blueStyleOption =
-        ListUserStyleSetting.ListOption(Option.Id("bluestyle"), "Blue", icon = null)
+        ListUserStyleSetting.ListOption(Option.Id("blue_style"), "Blue", icon = null)
 
     private val colorStyleList = listOf(redStyleOption, greenStyleOption, blueStyleOption)
 
@@ -195,7 +195,7 @@
         val userStyle = UserStyle(
             UserStyleData(
                 mapOf(
-                    "color_style_setting" to "bluestyle".encodeToByteArray(),
+                    "color_style_setting" to "blue_style".encodeToByteArray(),
                     "hand_style_setting" to "gothic_style".encodeToByteArray()
                 )
             ),
@@ -203,7 +203,7 @@
         )
 
         assertThat(userStyle.selectedOptions[colorStyleSetting]!!.id.value.decodeToString())
-            .isEqualTo("bluestyle")
+            .isEqualTo("blue_style")
         assertThat(userStyle.selectedOptions[watchHandStyleSetting]!!.id.value.decodeToString())
             .isEqualTo("gothic_style")
     }
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
index 357ab40..aa3216b 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
@@ -70,7 +70,7 @@
                                 icon = null
                             ),
                             ListUserStyleSetting.ListOption(
-                                Option.Id("bluestyle"),
+                                Option.Id("blue_style"),
                                 "Blue",
                                 icon = null
                             )
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 67ca8cf..b895934 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -132,7 +132,7 @@
         ListUserStyleSetting.ListOption(Option.Id("green_style"), "Green", icon = null)
 
     private val blueStyleOption =
-        ListUserStyleSetting.ListOption(Option.Id("bluestyle"), "Blue", icon = null)
+        ListUserStyleSetting.ListOption(Option.Id("blue_style"), "Blue", icon = null)
 
     private val colorStyleList = listOf(redStyleOption, greenStyleOption, blueStyleOption)
 
diff --git a/wear/wear/src/main/res/values-af/strings.xml b/wear/wear/src/main/res/values-af/strings.xml
index a0b1d41..a5e4ad237 100644
--- a/wear/wear/src/main/res/values-af/strings.xml
+++ b/wear/wear/src/main/res/values-af/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigasielaai"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Handelinglaai"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Sukses"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Mislukking"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Op jou foon oopgemaak"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Prent"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-am/strings.xml b/wear/wear/src/main/res/values-am/strings.xml
index e939f78..c0e7c27 100644
--- a/wear/wear/src/main/res/values-am/strings.xml
+++ b/wear/wear/src/main/res/values-am/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"የአሰሳ መሳቢያ"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"የእርምጃ መሳቢያ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"ስኬት"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"አልተሳካም"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"በስልክዎ ላይ ተከፍቷል"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ምስል"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ar/strings.xml b/wear/wear/src/main/res/values-ar/strings.xml
index 6f3fe90..21a9f90 100644
--- a/wear/wear/src/main/res/values-ar/strings.xml
+++ b/wear/wear/src/main/res/values-ar/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"لائحة التنقل"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"دُرج الإجراءات"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"تم تنفيذ الإجراء بنجاح."</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"تعذَّر إتمام العملية."</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"تم الفتح على هاتفك."</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"صورة"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-as/strings.xml b/wear/wear/src/main/res/values-as/strings.xml
index e905547..a0aa62c 100644
--- a/wear/wear/src/main/res/values-as/strings.xml
+++ b/wear/wear/src/main/res/values-as/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"নেভিগেশ্বন ড্ৰৱাৰ"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"কাৰ্য ড্ৰৱাৰ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"সফল হৈছে"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"বিফল হৈছে"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"আপোনাৰ ফ’নত খোলা হৈছে"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"প্ৰতিচ্ছবি"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-az/strings.xml b/wear/wear/src/main/res/values-az/strings.xml
index 74ccfe8..8998fbf 100644
--- a/wear/wear/src/main/res/values-az/strings.xml
+++ b/wear/wear/src/main/res/values-az/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Naviqasiya qutusu"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Əməliyyat qutusu"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Uğurlu oldu"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Alınmadı"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Telefonunuzda açıldı"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Şəkil"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-b+sr+Latn/strings.xml b/wear/wear/src/main/res/values-b+sr+Latn/strings.xml
index c58868b..107f542 100644
--- a/wear/wear/src/main/res/values-b+sr+Latn/strings.xml
+++ b/wear/wear/src/main/res/values-b+sr+Latn/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Fioka za navigaciju"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Fioka za radnju"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Uspelo je"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Greška"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Otvoreno je na telefonu"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Slika"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-be/strings.xml b/wear/wear/src/main/res/values-be/strings.xml
index 0fe9468..b9b7eaa 100644
--- a/wear/wear/src/main/res/values-be/strings.xml
+++ b/wear/wear/src/main/res/values-be/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Высоўнае меню навігацыі"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Высоўнае меню дзеянняў"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Выканана"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Памылка"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Адкрыта на тэлефоне"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Відарыс"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-bg/strings.xml b/wear/wear/src/main/res/values-bg/strings.xml
index 4330806..8f96402 100644
--- a/wear/wear/src/main/res/values-bg/strings.xml
+++ b/wear/wear/src/main/res/values-bg/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Слой за навигация"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Слой за действия"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Успешно"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Неуспешно"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Отворено на телефона ви"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Изображение"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-bn/strings.xml b/wear/wear/src/main/res/values-bn/strings.xml
index 66d6073..db4cb26 100644
--- a/wear/wear/src/main/res/values-bn/strings.xml
+++ b/wear/wear/src/main/res/values-bn/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"নেভিগেশন ড্রয়ার"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"অ্যাকশন ড্রয়ার"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"কাজটি সম্পূর্ণ হয়েছে"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"কাজটি করা যায়নি"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"আপনার ফোনে খোলা হয়েছে"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ছবি"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-bs/strings.xml b/wear/wear/src/main/res/values-bs/strings.xml
index aee1387..ee58387 100644
--- a/wear/wear/src/main/res/values-bs/strings.xml
+++ b/wear/wear/src/main/res/values-bs/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Ladica za navigaciju"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Ladica za radnju"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Uspješno"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Neuspješno"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Otvoreno na telefonu"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Slika"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ca/strings.xml b/wear/wear/src/main/res/values-ca/strings.xml
index 38972ba..b490b00 100644
--- a/wear/wear/src/main/res/values-ca/strings.xml
+++ b/wear/wear/src/main/res/values-ca/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Tauler de navegació"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Tauler d\'accions"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Correcte"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Error"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"S\'ha obert al telèfon"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imatge"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-cs/strings.xml b/wear/wear/src/main/res/values-cs/strings.xml
index d37c363..a1bcbf6 100644
--- a/wear/wear/src/main/res/values-cs/strings.xml
+++ b/wear/wear/src/main/res/values-cs/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Vysouvací panel navigace"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Vysouvací panel akcí"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Úspěch"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Selhání"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Otevřeno v telefonu"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Obrázek"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-da/strings.xml b/wear/wear/src/main/res/values-da/strings.xml
index 32691d8..fc4545d 100644
--- a/wear/wear/src/main/res/values-da/strings.xml
+++ b/wear/wear/src/main/res/values-da/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Sidemenu"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Handlingsmenu"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Gennemført"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Fejl"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Åbnet på din telefon"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Billede"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-de/strings.xml b/wear/wear/src/main/res/values-de/strings.xml
index 97734c7..11410bf 100644
--- a/wear/wear/src/main/res/values-de/strings.xml
+++ b/wear/wear/src/main/res/values-de/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigationsleiste"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Aktionsleiste"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Fertig"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Fehler"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Wurde auf dem Smartphone geöffnet"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Bild"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-el/strings.xml b/wear/wear/src/main/res/values-el/strings.xml
index 25cff0c..347fc44 100644
--- a/wear/wear/src/main/res/values-el/strings.xml
+++ b/wear/wear/src/main/res/values-el/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Συρτάρι πλοήγησης"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Συρτάρι ενεργειών"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Επιτυχία"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Αποτυχία"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Άνοιξε στο τηλέφωνό σας"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Εικόνα"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-en-rAU/strings.xml b/wear/wear/src/main/res/values-en-rAU/strings.xml
index 53d9019..136d9c9 100644
--- a/wear/wear/src/main/res/values-en-rAU/strings.xml
+++ b/wear/wear/src/main/res/values-en-rAU/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigation drawer"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Action drawer"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Success"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Failure"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Opened on your phone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Image"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-en-rCA/strings.xml b/wear/wear/src/main/res/values-en-rCA/strings.xml
index 53d9019..136d9c9 100644
--- a/wear/wear/src/main/res/values-en-rCA/strings.xml
+++ b/wear/wear/src/main/res/values-en-rCA/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigation drawer"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Action drawer"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Success"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Failure"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Opened on your phone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Image"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-en-rGB/strings.xml b/wear/wear/src/main/res/values-en-rGB/strings.xml
index 53d9019..136d9c9 100644
--- a/wear/wear/src/main/res/values-en-rGB/strings.xml
+++ b/wear/wear/src/main/res/values-en-rGB/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigation drawer"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Action drawer"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Success"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Failure"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Opened on your phone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Image"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-en-rIN/strings.xml b/wear/wear/src/main/res/values-en-rIN/strings.xml
index 53d9019..136d9c9 100644
--- a/wear/wear/src/main/res/values-en-rIN/strings.xml
+++ b/wear/wear/src/main/res/values-en-rIN/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigation drawer"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Action drawer"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Success"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Failure"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Opened on your phone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Image"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-en-rXC/strings.xml b/wear/wear/src/main/res/values-en-rXC/strings.xml
index e20077ec..3416c26 100644
--- a/wear/wear/src/main/res/values-en-rXC/strings.xml
+++ b/wear/wear/src/main/res/values-en-rXC/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎Navigation drawer‎‏‎‎‏‎"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎Action drawer‎‏‎‎‏‎"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎Success‎‏‎‎‏‎"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎Failure‎‏‎‎‏‎"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎Opened on your phone‎‏‎‎‏‎"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎Image‎‏‎‎‏‎"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-es-rUS/strings.xml b/wear/wear/src/main/res/values-es-rUS/strings.xml
index 9afe40c..7317e2e 100644
--- a/wear/wear/src/main/res/values-es-rUS/strings.xml
+++ b/wear/wear/src/main/res/values-es-rUS/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panel lateral de navegación"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panel lateral de acciones"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Listo"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Error"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Se abrió en el teléfono"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imagen"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-es/strings.xml b/wear/wear/src/main/res/values-es/strings.xml
index ad29e18..38366d47b 100644
--- a/wear/wear/src/main/res/values-es/strings.xml
+++ b/wear/wear/src/main/res/values-es/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panel de navegación"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panel de acciones"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Correcto"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Error"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Abierto en el teléfono"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imagen"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-et/strings.xml b/wear/wear/src/main/res/values-et/strings.xml
index 2dff6b1..26f85b4 100644
--- a/wear/wear/src/main/res/values-et/strings.xml
+++ b/wear/wear/src/main/res/values-et/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigeerimissahtel"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Toimingusahtel"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Õnnestus"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Ebaõnnestus"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Avatud teie telefonis"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Pilt"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-eu/strings.xml b/wear/wear/src/main/res/values-eu/strings.xml
index df7e326..91500a7 100644
--- a/wear/wear/src/main/res/values-eu/strings.xml
+++ b/wear/wear/src/main/res/values-eu/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Nabigazio-panel lerrakorra"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Ekintza-panel lerrakorra"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Gauzatu da ekintza"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Hutsegitea"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Telefonoan ireki da"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Irudia"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-fa/strings.xml b/wear/wear/src/main/res/values-fa/strings.xml
index d2f3e66..b7eeff2 100644
--- a/wear/wear/src/main/res/values-fa/strings.xml
+++ b/wear/wear/src/main/res/values-fa/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"کشوی پیمایش"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"کشوی فعالیت"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"موفق"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"ناموفق"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"در تلفنتان باز شد"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"تصویر"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-fi/strings.xml b/wear/wear/src/main/res/values-fi/strings.xml
index 0216994..9de2f6b 100644
--- a/wear/wear/src/main/res/values-fi/strings.xml
+++ b/wear/wear/src/main/res/values-fi/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigoinnin vetopaneeli"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Toimintojen vetopaneeli"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Onnistui"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Epäonnistui"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Avattiin puhelimellasi"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Kuva"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-fr-rCA/strings.xml b/wear/wear/src/main/res/values-fr-rCA/strings.xml
index bb3a2ffb..f1a1db8 100644
--- a/wear/wear/src/main/res/values-fr-rCA/strings.xml
+++ b/wear/wear/src/main/res/values-fr-rCA/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panneau de navigation"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panneau d\'actions"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Réussite"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Échec"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Ouverte sur votre téléphone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Image"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-fr/strings.xml b/wear/wear/src/main/res/values-fr/strings.xml
index bea0b78..43c0f1b 100644
--- a/wear/wear/src/main/res/values-fr/strings.xml
+++ b/wear/wear/src/main/res/values-fr/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panneau de navigation"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panneau de commandes"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Opération réussie"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Échec"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Ouvert sur votre téléphone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Image"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-gl/strings.xml b/wear/wear/src/main/res/values-gl/strings.xml
index e6247d1..6e2faa6 100644
--- a/wear/wear/src/main/res/values-gl/strings.xml
+++ b/wear/wear/src/main/res/values-gl/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panel de navegación"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panel de accións"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Abriuse"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Produciuse un erro"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Abriuse no teléfono"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imaxe"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-gu/strings.xml b/wear/wear/src/main/res/values-gu/strings.xml
index 5fd6cf7..ecb9659 100644
--- a/wear/wear/src/main/res/values-gu/strings.xml
+++ b/wear/wear/src/main/res/values-gu/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"નૅવિગેશન ડ્રોઅર"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ઍક્શન ડ્રોઅર"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"સફળ રહ્યાં"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"નિષ્ફળ રહ્યાં"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"તમારા ફોન પર ખોલી"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"છબી"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-hi/strings.xml b/wear/wear/src/main/res/values-hi/strings.xml
index 1454fc1..fdb74dd 100644
--- a/wear/wear/src/main/res/values-hi/strings.xml
+++ b/wear/wear/src/main/res/values-hi/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"नेविगेशन पैनल"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"कार्रवाई पैनल"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"कार्रवाई सफल रही"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"कार्रवाई सफल नहीं हुई"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"आपके फ़ोन पर खोला गया"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"इमेज"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-hr/strings.xml b/wear/wear/src/main/res/values-hr/strings.xml
index aee1387..337fc59 100644
--- a/wear/wear/src/main/res/values-hr/strings.xml
+++ b/wear/wear/src/main/res/values-hr/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Ladica za navigaciju"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Ladica za radnju"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Uspjelo je"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Nije uspjelo"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Otvoreno na telefonu"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Slika"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-hu/strings.xml b/wear/wear/src/main/res/values-hu/strings.xml
index f77c207..1aeab0e 100644
--- a/wear/wear/src/main/res/values-hu/strings.xml
+++ b/wear/wear/src/main/res/values-hu/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigációs fiók"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Műveleti fiók"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Sikerült"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Hiba"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Megnyitva a telefonján"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Kép"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-hy/strings.xml b/wear/wear/src/main/res/values-hy/strings.xml
index c5fa5df..11ae7b6 100644
--- a/wear/wear/src/main/res/values-hy/strings.xml
+++ b/wear/wear/src/main/res/values-hy/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Նավիգացիայի դարակ"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Գործողությունների դարակ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Պատրաստ է"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Ձախողում"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Բացվել է ձեր հեռախոսում"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Պատկեր"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-in/strings.xml b/wear/wear/src/main/res/values-in/strings.xml
index 8c28e14..e122273 100644
--- a/wear/wear/src/main/res/values-in/strings.xml
+++ b/wear/wear/src/main/res/values-in/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panel navigasi"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panel samping tindakan"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Berhasil"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Gagal"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Dibuka di ponsel"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Gambar"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-is/strings.xml b/wear/wear/src/main/res/values-is/strings.xml
index 2fc00e4..44721f5 100644
--- a/wear/wear/src/main/res/values-is/strings.xml
+++ b/wear/wear/src/main/res/values-is/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Yfirlitsskúffa"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Aðgerðaskúffa"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Tókst"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Tókst ekki"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Opnað í símanum þínum"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Mynd"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-it/strings.xml b/wear/wear/src/main/res/values-it/strings.xml
index 4e9efb0..65e3d3b 100644
--- a/wear/wear/src/main/res/values-it/strings.xml
+++ b/wear/wear/src/main/res/values-it/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Riquadro di navigazione a scomparsa"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Riquadro a scomparsa delle azioni"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Operazione riuscita"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Errore"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Elemento aperto sul telefono"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Immagine"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-iw/strings.xml b/wear/wear/src/main/res/values-iw/strings.xml
index 6bcd69b..a07ab35 100644
--- a/wear/wear/src/main/res/values-iw/strings.xml
+++ b/wear/wear/src/main/res/values-iw/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"חלונית הזזה לניווט"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"חלונית הזזה לפעולות"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"ההפעלה הצליחה"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"נכשלה"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"נפתחה בטלפון שלך"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"תמונה"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ja/strings.xml b/wear/wear/src/main/res/values-ja/strings.xml
index d5400eb..8fad2b8 100644
--- a/wear/wear/src/main/res/values-ja/strings.xml
+++ b/wear/wear/src/main/res/values-ja/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ナビゲーション ドロワー"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"アクション ドロワー"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"完了"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"エラー"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"スマートフォンで開きました"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"画像"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ka/strings.xml b/wear/wear/src/main/res/values-ka/strings.xml
index 9b1f566..6218c34 100644
--- a/wear/wear/src/main/res/values-ka/strings.xml
+++ b/wear/wear/src/main/res/values-ka/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ნავიგაციის უჯრა"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ქმედების უჯრა"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"წარმატება"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"შეფერხება"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"გახსნილია თქვენს ტელეფონში"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"სურათი"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-kk/strings.xml b/wear/wear/src/main/res/values-kk/strings.xml
index fe96962..9356103 100644
--- a/wear/wear/src/main/res/values-kk/strings.xml
+++ b/wear/wear/src/main/res/values-kk/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Навигация тартпасы"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Әрекеттер тартпасы"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Орындалды."</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Орындалмады."</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Телефонда ашылды."</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Сурет"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-km/strings.xml b/wear/wear/src/main/res/values-km/strings.xml
index 70abd5d..edead15 100644
--- a/wear/wear/src/main/res/values-km/strings.xml
+++ b/wear/wear/src/main/res/values-km/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ថត​រុករក"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ថត​សកម្មភាព"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"ជោគជ័យ"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"បរាជ័យ"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"បានបើក​នៅលើ​ទូរសព្ទ​របស់អ្នក"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"រូបភាព"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-kn/strings.xml b/wear/wear/src/main/res/values-kn/strings.xml
index 0b9669c..1272aa1 100644
--- a/wear/wear/src/main/res/values-kn/strings.xml
+++ b/wear/wear/src/main/res/values-kn/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ನ್ಯಾವಿಗೇಶನ್ ಡ್ರಾಯರ್"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ಕ್ರಿಯೆ ಡ್ರಾಯರ್"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"ಯಶಸ್ವಿಯಾಗಿದೆ"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"ವೈಫಲ್ಯ"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ತೆರೆಯಲಾಗಿದೆ"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ಚಿತ್ರ"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ko/strings.xml b/wear/wear/src/main/res/values-ko/strings.xml
index 4f93d4a..f84d254 100644
--- a/wear/wear/src/main/res/values-ko/strings.xml
+++ b/wear/wear/src/main/res/values-ko/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"탐색 창"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"작업 창"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"완료"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"실패"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"휴대전화에서 열림"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"이미지"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ky/strings.xml b/wear/wear/src/main/res/values-ky/strings.xml
index 6324aa7..81ac8ea 100644
--- a/wear/wear/src/main/res/values-ky/strings.xml
+++ b/wear/wear/src/main/res/values-ky/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Чабыттоо суурмасы"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Аракет суурмасы"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Ийгилик"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Ката"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Саатыңызда ачылды"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Сүрөт"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-lo/strings.xml b/wear/wear/src/main/res/values-lo/strings.xml
index a405fa6..cdf495f 100644
--- a/wear/wear/src/main/res/values-lo/strings.xml
+++ b/wear/wear/src/main/res/values-lo/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ແຖບການນຳທາງ"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ແຖບຄຳສັ່ງ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"ສໍາເລັດແລ້ວ"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"ບໍ່ສຳເລັດ"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"ເປີດຢູ່ໂທລະສັບຂອງທ່ານແລ້ວ"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ຮູບ"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-lt/strings.xml b/wear/wear/src/main/res/values-lt/strings.xml
index 2ea598f..65b0019 100644
--- a/wear/wear/src/main/res/values-lt/strings.xml
+++ b/wear/wear/src/main/res/values-lt/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Naršymo skydelis"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Veiksmo skydelis"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Pavyko"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Nepavyko"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Atidaryta jūsų telefone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Vaizdas"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-lv/strings.xml b/wear/wear/src/main/res/values-lv/strings.xml
index b34c874..cfa6590 100644
--- a/wear/wear/src/main/res/values-lv/strings.xml
+++ b/wear/wear/src/main/res/values-lv/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigācijas atvilktne"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Darbību atvilktne"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Gatavs"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Kļūme"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Atvērts jūsu tālrunī"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Attēls"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-mk/strings.xml b/wear/wear/src/main/res/values-mk/strings.xml
index dbc5bac..0ed3a32 100644
--- a/wear/wear/src/main/res/values-mk/strings.xml
+++ b/wear/wear/src/main/res/values-mk/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Фиока за навигација"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Фиока за дејство"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Успешно"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Неуспешно"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Отворено на телефонот"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Слика"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ml/strings.xml b/wear/wear/src/main/res/values-ml/strings.xml
index f7347c0..8b8fc01 100644
--- a/wear/wear/src/main/res/values-ml/strings.xml
+++ b/wear/wear/src/main/res/values-ml/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"നാവിഗേഷൻ ഡ്രോയർ"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ആക്ഷൻ ഡ്രോയർ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"വിജയകരം"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"പരാജയം"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"നിങ്ങളുടെ ഫോണിൽ തുറന്നത്"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ചിത്രം"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-mn/strings.xml b/wear/wear/src/main/res/values-mn/strings.xml
index 8728919..84b053f 100644
--- a/wear/wear/src/main/res/values-mn/strings.xml
+++ b/wear/wear/src/main/res/values-mn/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Навигацын шургуулга"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Үйлдлийн татуурга"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Амжилттай"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Амжилтгүй"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Таны утсан дээр нээсэн"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Зураг"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-mr/strings.xml b/wear/wear/src/main/res/values-mr/strings.xml
index 3a2e7a1..85b0637 100644
--- a/wear/wear/src/main/res/values-mr/strings.xml
+++ b/wear/wear/src/main/res/values-mr/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"नेव्हिगेशन ड्रॉवर"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"क्रिया ड्रॉवर"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"यशस्वीरीत्या पूर्ण केली"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"पूर्ण करता आली नाही"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"तुमच्या फोनवर उघडले"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"इमेज"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ms/strings.xml b/wear/wear/src/main/res/values-ms/strings.xml
index d64a574..4dc70c4 100644
--- a/wear/wear/src/main/res/values-ms/strings.xml
+++ b/wear/wear/src/main/res/values-ms/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Laci navigasi"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Laci tindakan"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Berjaya"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Gagal"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Dibuka pada telefon anda"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imej"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-my/strings.xml b/wear/wear/src/main/res/values-my/strings.xml
index ca60daf..54b4b34 100644
--- a/wear/wear/src/main/res/values-my/strings.xml
+++ b/wear/wear/src/main/res/values-my/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"လမ်းကြောင်းပြ အံဆွဲ"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"လုပ်ဆောင်ချက် အံဆွဲ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"အောင်မြင်ပါသည်"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"မအောင်မြင်ပါ"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"သင့်ဖုန်းတွင် ဖွင့်ထားသည်"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ပုံ"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-nb/strings.xml b/wear/wear/src/main/res/values-nb/strings.xml
index b02525d..f6deeb2 100644
--- a/wear/wear/src/main/res/values-nb/strings.xml
+++ b/wear/wear/src/main/res/values-nb/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Uttrekksmeny"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Uttrekksmeny for handlinger"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Fullført"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Mislyktes"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Åpnet på telefonen"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Bilde"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ne/strings.xml b/wear/wear/src/main/res/values-ne/strings.xml
index b8527a3..7a3783b 100644
--- a/wear/wear/src/main/res/values-ne/strings.xml
+++ b/wear/wear/src/main/res/values-ne/strings.xml
@@ -18,4 +18,12 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"नेभिगेसन ड्रअर"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"कारबाहीसम्बन्धी ड्रअर"</string>
+    <!-- no translation found for confirmation_overlay_a11y_description_success (2936753235333429892) -->
+    <skip />
+    <!-- no translation found for confirmation_overlay_a11y_description_fail (6412144553299494336) -->
+    <skip />
+    <!-- no translation found for confirmation_overlay_a11y_description_phone (8809042819843332546) -->
+    <skip />
+    <!-- no translation found for confirmation_overlay_a11y_type_image (4327971892987955014) -->
+    <skip />
 </resources>
diff --git a/wear/wear/src/main/res/values-nl/strings.xml b/wear/wear/src/main/res/values-nl/strings.xml
index 760346d..da5ace8 100644
--- a/wear/wear/src/main/res/values-nl/strings.xml
+++ b/wear/wear/src/main/res/values-nl/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Zijmenu"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Actiemenu"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Gelukt"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Fout"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Geopend op je telefoon"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Afbeelding"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-or/strings.xml b/wear/wear/src/main/res/values-or/strings.xml
index a01f316..36176cb 100644
--- a/wear/wear/src/main/res/values-or/strings.xml
+++ b/wear/wear/src/main/res/values-or/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ନେଭିଗେଶନ୍ ପ୍ୟାନେଲ୍"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"କାର୍ଯ୍ୟକାରୀ ପ୍ୟାନେଲ୍"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"ସଫଳ ହୋଇଛି"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"ବିଫଳ ହୋଇଛି"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"ଆପଣଙ୍କ ଫୋନରେ ଖୋଲାଯାଇଛି"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ଛବି"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-pa/strings.xml b/wear/wear/src/main/res/values-pa/strings.xml
index f13d106..16298fa 100644
--- a/wear/wear/src/main/res/values-pa/strings.xml
+++ b/wear/wear/src/main/res/values-pa/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਡ੍ਰਾਅਰ"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ਕਾਰਵਾਈ ਡ੍ਰਾਅਰ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"ਸਫਲਤਾ"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"ਅਸਫਲਤਾ"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਖੋਲ੍ਹਿਆ ਗਿਆ"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ਚਿੱਤਰ"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-pl/strings.xml b/wear/wear/src/main/res/values-pl/strings.xml
index fcb9b56..619928e 100644
--- a/wear/wear/src/main/res/values-pl/strings.xml
+++ b/wear/wear/src/main/res/values-pl/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panel nawigacji"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panel działań"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Udało się"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Błąd"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Otwarto na telefonie"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Obraz"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-pt-rBR/strings.xml b/wear/wear/src/main/res/values-pt-rBR/strings.xml
index 46cf12e..59cf852 100644
--- a/wear/wear/src/main/res/values-pt-rBR/strings.xml
+++ b/wear/wear/src/main/res/values-pt-rBR/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Gaveta de navegação"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Gaveta de ações"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Pronto"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Falha"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Item aberto no seu smartphone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imagem"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-pt-rPT/strings.xml b/wear/wear/src/main/res/values-pt-rPT/strings.xml
index 46cf12e..053696a 100644
--- a/wear/wear/src/main/res/values-pt-rPT/strings.xml
+++ b/wear/wear/src/main/res/values-pt-rPT/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Gaveta de navegação"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Gaveta de ações"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Êxito"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Falha"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Aberto no seu telemóvel"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imagem"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-pt/strings.xml b/wear/wear/src/main/res/values-pt/strings.xml
index 46cf12e..59cf852 100644
--- a/wear/wear/src/main/res/values-pt/strings.xml
+++ b/wear/wear/src/main/res/values-pt/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Gaveta de navegação"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Gaveta de ações"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Pronto"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Falha"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Item aberto no seu smartphone"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imagem"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ro/strings.xml b/wear/wear/src/main/res/values-ro/strings.xml
index 1cd2faf..a94e3ff 100644
--- a/wear/wear/src/main/res/values-ro/strings.xml
+++ b/wear/wear/src/main/res/values-ro/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Panou de navigare"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Panou de acțiune"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Succes"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Nereușită"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"S-a deschis pe telefon"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Imagine"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ru/strings.xml b/wear/wear/src/main/res/values-ru/strings.xml
index 226b67a..f2399d9 100644
--- a/wear/wear/src/main/res/values-ru/strings.xml
+++ b/wear/wear/src/main/res/values-ru/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Панель навигации"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Панель действий"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Готово"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Ошибка"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Открыто на телефоне"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Изображение"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-si/strings.xml b/wear/wear/src/main/res/values-si/strings.xml
index 2ce2823..cef6edd 100644
--- a/wear/wear/src/main/res/values-si/strings.xml
+++ b/wear/wear/src/main/res/values-si/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"සංචාලන ලාච්චුව"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ක්‍රියාමාර්ග ලාච්චුව"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"සාර්ථකයි"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"අසාර්ථකයි"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"ඔබගේ දුරකථනයෙහි විවෘත කරන ලදි"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"රූපය"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-sk/strings.xml b/wear/wear/src/main/res/values-sk/strings.xml
index aa39b5f..250e0e14 100644
--- a/wear/wear/src/main/res/values-sk/strings.xml
+++ b/wear/wear/src/main/res/values-sk/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigačný vysúvací panel"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Vysúvací panel akcií"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Úspech"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Nepodarilo sa"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Otvorené v telefóne"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Obrázok"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-sl/strings.xml b/wear/wear/src/main/res/values-sl/strings.xml
index 75ed0ee..9251c1c 100644
--- a/wear/wear/src/main/res/values-sl/strings.xml
+++ b/wear/wear/src/main/res/values-sl/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Predal za krmarjenje"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Predal z dejanji"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Uspelo je."</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Napaka"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Odprto v telefonu"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Slika"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-sq/strings.xml b/wear/wear/src/main/res/values-sq/strings.xml
index 3a0d059..494e982 100644
--- a/wear/wear/src/main/res/values-sq/strings.xml
+++ b/wear/wear/src/main/res/values-sq/strings.xml
@@ -18,4 +18,12 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Sirtari i navigimit"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Sirtari i veprimit"</string>
+    <!-- no translation found for confirmation_overlay_a11y_description_success (2936753235333429892) -->
+    <skip />
+    <!-- no translation found for confirmation_overlay_a11y_description_fail (6412144553299494336) -->
+    <skip />
+    <!-- no translation found for confirmation_overlay_a11y_description_phone (8809042819843332546) -->
+    <skip />
+    <!-- no translation found for confirmation_overlay_a11y_type_image (4327971892987955014) -->
+    <skip />
 </resources>
diff --git a/wear/wear/src/main/res/values-sr/strings.xml b/wear/wear/src/main/res/values-sr/strings.xml
index 11cf001..b1e696e 100644
--- a/wear/wear/src/main/res/values-sr/strings.xml
+++ b/wear/wear/src/main/res/values-sr/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Фиока за навигацију"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Фиока за радњу"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Успело је"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Грешка"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Отворено је на телефону"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Слика"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-sv/strings.xml b/wear/wear/src/main/res/values-sv/strings.xml
index 4dddc5e..bd8dc5c 100644
--- a/wear/wear/src/main/res/values-sv/strings.xml
+++ b/wear/wear/src/main/res/values-sv/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigeringspanel"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Åtgärdspanel"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Klart"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Fel"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Öppnades på telefonen"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Bild"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-sw/strings.xml b/wear/wear/src/main/res/values-sw/strings.xml
index 4244cf4..df1df50 100644
--- a/wear/wear/src/main/res/values-sw/strings.xml
+++ b/wear/wear/src/main/res/values-sw/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Droo ya kusogeza"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Droo ya vitendo"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Imefaulu"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Imeshindwa"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Imefunguliwa kwenye simu yako"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Picha"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ta/strings.xml b/wear/wear/src/main/res/values-ta/strings.xml
index 4149d10..e696fe6 100644
--- a/wear/wear/src/main/res/values-ta/strings.xml
+++ b/wear/wear/src/main/res/values-ta/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"வழிசெலுத்தல் டிராயர்"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"செயல் டிராயர்"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"திறக்கப்பட்டது"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"திறக்க முடியவில்லை"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"உங்கள் மொபைலில் திறக்கப்பட்டது"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"படம்"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-te/strings.xml b/wear/wear/src/main/res/values-te/strings.xml
index c288710..16cb784 100644
--- a/wear/wear/src/main/res/values-te/strings.xml
+++ b/wear/wear/src/main/res/values-te/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"నావిగేషన్ డ్రాయర్"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"చర్య డ్రాయర్"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"విజయవంతమైంది"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"వైఫల్యం"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"మీ ఫోన్‌లో తెరవబడింది"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"ఇమేజ్"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-th/strings.xml b/wear/wear/src/main/res/values-th/strings.xml
index ea2a130..79fd276 100644
--- a/wear/wear/src/main/res/values-th/strings.xml
+++ b/wear/wear/src/main/res/values-th/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"ลิ้นชักการนำทาง"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"ลิ้นชักการดำเนินการ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"สำเร็จ"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"ไม่สำเร็จ"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"เปิดในโทรศัพท์ของคุณแล้ว"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"รูปภาพ"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-tl/strings.xml b/wear/wear/src/main/res/values-tl/strings.xml
index 53d9019..62c0b9d 100644
--- a/wear/wear/src/main/res/values-tl/strings.xml
+++ b/wear/wear/src/main/res/values-tl/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigation drawer"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Action drawer"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Matagumpay"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Hindi Nagtagumpay"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Binuksan sa iyong telepono"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Larawan"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-tr/strings.xml b/wear/wear/src/main/res/values-tr/strings.xml
index ccfd319..b3747b1 100644
--- a/wear/wear/src/main/res/values-tr/strings.xml
+++ b/wear/wear/src/main/res/values-tr/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Gezinme çekmecesi"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"İşlem çekmecesi"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Başarılı"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Başarısız"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Telefonunuzda açıldı"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Resim"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-uk/strings.xml b/wear/wear/src/main/res/values-uk/strings.xml
index cfe9164..9d84935 100644
--- a/wear/wear/src/main/res/values-uk/strings.xml
+++ b/wear/wear/src/main/res/values-uk/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Панель навігації"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Висувна панель команд"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Готово"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Помилка"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Відкрито на вашому телефоні"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Зображення"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-ur/strings.xml b/wear/wear/src/main/res/values-ur/strings.xml
index 0756b77..c18ee62 100644
--- a/wear/wear/src/main/res/values-ur/strings.xml
+++ b/wear/wear/src/main/res/values-ur/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"نیویگیشن دراز"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"کارروائی دراز"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"کامیاب"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"ناکام"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"آپ کے فون پر کھول دی گئی"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"تصویر"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-uz/strings.xml b/wear/wear/src/main/res/values-uz/strings.xml
index bb4bf3b..633d3ef 100644
--- a/wear/wear/src/main/res/values-uz/strings.xml
+++ b/wear/wear/src/main/res/values-uz/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Navigatsiya paneli"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Buyruqlar paneli"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Tayyor"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Xatolik"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Telefoningizda ochildi"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Rasm"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-vi/strings.xml b/wear/wear/src/main/res/values-vi/strings.xml
index 6625a06a..c353e88 100644
--- a/wear/wear/src/main/res/values-vi/strings.xml
+++ b/wear/wear/src/main/res/values-vi/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Ngăn điều hướng"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Ngăn tác vụ"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Thành công"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Lỗi"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Đã mở trên điện thoại của bạn"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Hình ảnh"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-zh-rCN/strings.xml b/wear/wear/src/main/res/values-zh-rCN/strings.xml
index 3f532cd..e4622b2 100644
--- a/wear/wear/src/main/res/values-zh-rCN/strings.xml
+++ b/wear/wear/src/main/res/values-zh-rCN/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"抽屉式导航栏"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"操作抽屉式导航栏"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"成功"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"失败"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"已在您的手机上打开"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"图片"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-zh-rHK/strings.xml b/wear/wear/src/main/res/values-zh-rHK/strings.xml
index 0b64213..7a4679e 100644
--- a/wear/wear/src/main/res/values-zh-rHK/strings.xml
+++ b/wear/wear/src/main/res/values-zh-rHK/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"導覽列"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"操作導覽列"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"成功"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"失敗"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"已經喺手機度開咗"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"圖片"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-zh-rTW/strings.xml b/wear/wear/src/main/res/values-zh-rTW/strings.xml
index 4c0be1b..76a304e 100644
--- a/wear/wear/src/main/res/values-zh-rTW/strings.xml
+++ b/wear/wear/src/main/res/values-zh-rTW/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"導覽匣"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"動作導覽匣"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"成功"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"失敗"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"已在手機上開啟"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"圖片"</string>
 </resources>
diff --git a/wear/wear/src/main/res/values-zu/strings.xml b/wear/wear/src/main/res/values-zu/strings.xml
index 33c6483..3cdb355 100644
--- a/wear/wear/src/main/res/values-zu/strings.xml
+++ b/wear/wear/src/main/res/values-zu/strings.xml
@@ -18,4 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ws_navigation_drawer_content_description" msgid="777117228299084604">"Ikhabethe lokuzulazula"</string>
     <string name="ws_action_drawer_content_description" msgid="8863821639144537553">"Ikhabethe lesenzo"</string>
+    <string name="confirmation_overlay_a11y_description_success" msgid="2936753235333429892">"Impumelelo"</string>
+    <string name="confirmation_overlay_a11y_description_fail" msgid="6412144553299494336">"Ukwehluleka"</string>
+    <string name="confirmation_overlay_a11y_description_phone" msgid="8809042819843332546">"Ivuliwe kufoni yakho"</string>
+    <string name="confirmation_overlay_a11y_type_image" msgid="4327971892987955014">"Isithombe"</string>
 </resources>
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatDeviceTest.kt b/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatDeviceTest.kt
index 0d97b77..d38b131 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatDeviceTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatDeviceTest.kt
@@ -19,9 +19,12 @@
 package androidx.window.layout
 
 import android.content.Context
+import android.content.pm.ActivityInfo
+import androidx.test.core.app.ActivityScenario
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import androidx.window.TestConfigChangeHandlingActivity
 import androidx.window.WindowTestBase
 import androidx.window.core.Version
 import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
@@ -34,6 +37,9 @@
 import com.nhaarman.mockitokotlin2.atLeastOnce
 import com.nhaarman.mockitokotlin2.mock
 import com.nhaarman.mockitokotlin2.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runBlockingTest
 import org.junit.Assert.assertNotNull
 import org.junit.Assume.assumeTrue
 import org.junit.Before
@@ -47,6 +53,7 @@
  */
 @LargeTest
 @RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 public class SidecarCompatDeviceTest : WindowTestBase(), CompatDeviceTestInterface {
 
     private lateinit var sidecarCompat: SidecarCompat
@@ -73,6 +80,49 @@
         }
     }
 
+    @Test
+    fun testWindowLayoutCallbackOnConfigChange() {
+        val testScope = TestCoroutineScope()
+        testScope.runBlockingTest {
+            val scenario = ActivityScenario.launch(TestConfigChangeHandlingActivity::class.java)
+            val callbackInterface = mock<ExtensionCallbackInterface>()
+            scenario.onActivity { activity ->
+                val windowToken = getActivityWindowToken(activity)
+                assertNotNull(windowToken)
+                sidecarCompat.setExtensionCallback(callbackInterface)
+                sidecarCompat.onWindowLayoutChangeListenerAdded(activity)
+                activity.resetLayoutCounter()
+                activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+                activity.waitForLayout()
+            }
+            scenario.onActivity { activity ->
+                val windowToken = getActivityWindowToken(activity)
+                assertNotNull(windowToken)
+                val sidecarWindowLayoutInfo =
+                    sidecarCompat.sidecar!!.getWindowLayoutInfo(windowToken)
+                verify(callbackInterface, atLeastOnce()).onWindowLayoutChanged(
+                    any(),
+                    argThat(SidecarMatcher(sidecarWindowLayoutInfo))
+                )
+            }
+            scenario.onActivity { activity ->
+                activity.resetLayoutCounter()
+                activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+                activity.waitForLayout()
+            }
+            scenario.onActivity { activity ->
+                val windowToken = getActivityWindowToken(activity)
+                assertNotNull(windowToken)
+                val updatedSidecarWindowLayoutInfo =
+                    sidecarCompat.sidecar!!.getWindowLayoutInfo(windowToken)
+                verify(callbackInterface, atLeastOnce()).onWindowLayoutChanged(
+                    any(),
+                    argThat(SidecarMatcher(updatedSidecarWindowLayoutInfo))
+                )
+            }
+        }
+    }
+
     private fun assumeExtensionV01() {
         val sidecarVersion = SidecarCompat.sidecarVersion
         assumeTrue(Version.VERSION_0_1 == sidecarVersion)
diff --git a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoRepositoryImplTest.kt b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoRepositoryImplTest.kt
index 734d865..d73c471 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoRepositoryImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoRepositoryImplTest.kt
@@ -29,7 +29,6 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestCoroutineScope
 import kotlinx.coroutines.test.runBlockingTest
-import org.junit.Assert.assertEquals
 import org.junit.Rule
 import org.junit.Test
 import java.util.concurrent.Executor
@@ -91,6 +90,7 @@
                     WindowMetricsCalculatorCompat,
                     FakeWindowBackend()
                 )
+                activity.resetLayoutCounter()
                 activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
                 activity.waitForLayout()
                 testScope.launch {
@@ -98,6 +98,7 @@
                 }
             }
             scenario.onActivity { activity ->
+                activity.resetLayoutCounter()
                 activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
                 activity.waitForLayout()
             }
@@ -107,20 +108,6 @@
         }
 
     @Test
-    public fun testGetMaximumWindowMetrics() {
-        activityScenario.scenario.onActivity { testActivity ->
-            val repo = WindowInfoRepositoryImpl(
-                testActivity,
-                WindowMetricsCalculatorCompat,
-                FakeWindowBackend()
-            )
-            val expected = WindowMetricsCalculatorCompat.computeMaximumWindowMetrics(testActivity)
-            val actual = repo.maximumWindowMetrics
-            assertEquals(expected, actual)
-        }
-    }
-
-    @Test
     public fun testWindowLayoutFeatures(): Unit = testScope.runBlockingTest {
         activityScenario.scenario.onActivity { testActivity ->
             val windowMetricsCalculator = WindowMetricsCalculatorCompat
diff --git a/window/window/src/main/java/androidx/window/layout/SidecarCompat.kt b/window/window/src/main/java/androidx/window/layout/SidecarCompat.kt
index 6c97b75..4f60697 100644
--- a/window/window/src/main/java/androidx/window/layout/SidecarCompat.kt
+++ b/window/window/src/main/java/androidx/window/layout/SidecarCompat.kt
@@ -20,7 +20,9 @@
 
 import android.annotation.SuppressLint
 import android.app.Activity
+import android.content.ComponentCallbacks
 import android.content.Context
+import android.content.res.Configuration
 import android.os.IBinder
 import android.text.TextUtils
 import android.util.Log
@@ -51,6 +53,9 @@
     // Map of active listeners registered with #onWindowLayoutChangeListenerAdded() and not yet
     // removed by #onWindowLayoutChangeListenerRemoved().
     private val windowListenerRegisteredContexts = mutableMapOf<IBinder, Activity>()
+    // Map of activities registered to their component callbacks so we can keep track and
+    // remove when the activity is unregistered
+    private val componentCallbackMap = mutableMapOf<Activity, ComponentCallbacks>()
     private var extensionCallback: ExtensionCallbackInterface? = null
 
     constructor(context: Context) : this(
@@ -103,11 +108,36 @@
             sidecar?.onDeviceStateListenersChanged(false)
         }
         extensionCallback?.onWindowLayoutChanged(activity, getWindowLayoutInfo(activity))
+        registerConfigurationChangeListener(activity)
+    }
+
+    private fun registerConfigurationChangeListener(activity: Activity) {
+        // Only register a component callback if we haven't already as register
+        // may be called multiple times for the same activity
+        if (componentCallbackMap[activity] == null) {
+            // Create a configuration change observer to send updated WindowLayoutInfo
+            // when the configuration of the app changes: b/186647126
+            val configChangeObserver = object : ComponentCallbacks {
+                override fun onConfigurationChanged(newConfig: Configuration) {
+                    extensionCallback?.onWindowLayoutChanged(
+                        activity,
+                        getWindowLayoutInfo(activity)
+                    )
+                }
+
+                override fun onLowMemory() {
+                    return
+                }
+            }
+            componentCallbackMap[activity] = configChangeObserver
+            activity.registerComponentCallbacks(configChangeObserver)
+        }
     }
 
     override fun onWindowLayoutChangeListenerRemoved(activity: Activity) {
         val windowToken = getActivityWindowToken(activity) ?: return
         sidecar?.onWindowLayoutChangeListenerRemoved(windowToken)
+        unregisterComponentCallback(activity)
         val isLast = windowListenerRegisteredContexts.size == 1
         windowListenerRegisteredContexts.remove(windowToken)
         if (isLast) {
@@ -115,6 +145,12 @@
         }
     }
 
+    private fun unregisterComponentCallback(activity: Activity) {
+        val configChangeObserver = componentCallbackMap[activity]
+        activity.unregisterComponentCallbacks(configChangeObserver)
+        componentCallbackMap.remove(activity)
+    }
+
     @SuppressLint("BanUncheckedReflection")
     override fun validateExtensionInterface(): Boolean {
         return try {
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoRepositoryImpl.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoRepositoryImpl.kt
index 44ace44..b351b5e 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoRepositoryImpl.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoRepositoryImpl.kt
@@ -54,7 +54,6 @@
      * current bounds that the user has selected for the [Activity][android.app.Activity]'s
      * window.
      *
-     * @see maximumWindowMetrics
      * @see android.view.WindowManager.getCurrentWindowMetrics
      */
     override val currentWindowMetrics: Flow<WindowMetrics>
@@ -64,36 +63,6 @@
             }
         }
 
-    /**
-     * Returns the largest [WindowMetrics] an app may expect in the current system state.
-     *
-     *
-     * The metrics describe the size of the largest potential area the window might occupy with
-     * [MATCH_PARENT][android.view.WindowManager.LayoutParams.MATCH_PARENT] width and height
-     * and any combination of flags that would allow the window to extend behind display cutouts.
-     *
-     *
-     * The value of this is based on the largest **potential** windowing state of the system.
-     * For example, for activities in multi-window mode the metrics returned are based on what the
-     * bounds would be if the user expanded the window to cover the entire screen.
-     *
-     *
-     * Note that this might still be smaller than the size of the physical display if certain
-     * areas of the display are not available to windows created for the associated [Context].
-     * For example, devices with foldable displays that wrap around the enclosure may split the
-     * physical display into different regions, one for the front and one for the back, each acting
-     * as different logical displays. In this case [.getMaximumWindowMetrics] would return
-     * the region describing the side of the device the associated [context&#39;s][Context]
-     * window is placed.
-     *
-     * @see currentWindowMetrics
-     * @see android.view.WindowManager.getMaximumWindowMetrics
-     */
-    val maximumWindowMetrics: WindowMetrics
-        get() {
-            return windowMetricsCalculator.computeMaximumWindowMetrics(activity)
-        }
-
     private fun <T> configurationChanged(producer: () -> T): Flow<T> {
         return flow {
             val channel = Channel<T>(